likes
comments
collection
share

Js中异步代码挂起怎么解决?

作者站长头像
站长
· 阅读数 59

从下面代码引入问题

function a() {
    console.log('aa');
}

function b() {
    setTimeout(() => { //异步代码
        console.log('bb');
    }, 1000)
}

function c() {
    console.log('cc');
}

a()
b()
c()

上述代码的执行结果为先打印'aa',再打印'cc',等一秒后再打印'bb'。哎?我们是不是就有疑问了,我们明显是先调用的函数a,再调用的函数b,最后调用的函数c,为什么函数b的打印结果最后才出来呢?这里我们要清楚的是函数b中定义了一个计时器,执行此代码是需要时间的,属于异步代码,当浏览器执行到此代码时,会先将此程序挂起,继续往下执行,最后才会执行异步代码。那要怎么解决此类问题呢?一个方法是将其他函数体内也定义一个计时器,这样也就会按顺序调用了,但是这样太不优雅了;还一个方法是函数c作为参数传入函数b,在函数b中执行掉,这样也不优雅。es6出来后就可以使用promise来解决此问题了。

js是一种单线程语言

什么是单线程?

我们可以理解为一次只能完成一个任务,如果有其他任务进来,那就需要排队了,一个任务完成了接着下一个任务。

因为js是一种单线程语言,任务是按顺序执行的,但是有时我们有多个任务同时执行的需求,这就需要异步编程的思想。

什么是异步?

当客户端发送给服务端请求时,在等待服务端响应的时候,客户端可以做其他的事情。

什么是异步模式调用? 

前一个任务执行完,调用回调函数而不是进行后一个任务。后一个任务不等前一个任务结束就执行,任务排列顺序与执行顺序无关。

什么是回调函数?

把函数当作参数传入另一个函数中,不会立即执行,当需要用这个函数时,再回调运行()这个函数。

以前是通过回调函数实现异步的,但是回调用多了会出现回调地狱,导致爆栈。

举个用回调函数来解决异步代码挂起问题

<body>
    <div class="box">
        <audio src="" id="audio" controls></audio> </audio>
    </div>
    <script>
        //ajax
        let url = ''
        function getSong(cb) {
            $.ajax({
                url: ' 数据地址',
                dataType: 'json',
                success(res) {
                    console.log(res);
                    url = res[0].url
                    cb()
                }
            })
        }
        getSong(playSong)

        function playSong() {
            let audio = document.getElementById('audio')
            window.addEventListener('click', () => {
                audio.src = url
                window.onclick = function () {
                    audio.play()
                }
            })
        }

    </script>
</body>

代码中用ajax向后端获取数据,这是需要时间的,属于异步代码,当我们分开调用这两个函数,函数getSong中的异步代码会出现挂起状态,导致函数playSong中的url获取不到值,会出现报错的情况,运用回调函数可以很好地解决这个问题。

Promise的使用

先执行一段代码

function xq() {

    setTimeout(() => {
        console.log('老王');
    }, 2000)

}


function marry() {
    setTimeout(() => {
        console.log('老王结婚了');
    }, 1000)
}


function baby() {
    setTimeout(() => {
        console.log('小王出生了');
    }, 500)
}
xq()
marry()
baby()

结果为

Js中异步代码挂起怎么解决?

???这是不是有点违背了道德,只能说老王是个渣男。这时候我们就需要使用promise对象来调整一下顺序了。

function xq() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('老王去相亲');
            resolve('ok')
        }, 2000)
    })
}


function marry() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('老王结婚了');
            resolve('ok')
        }, 1000)
    })
}


function baby() {
    setTimeout(() => {
        console.log('小王出生了');
    }, 500)
}


// xq().then(() => {
//     marry().then(() => {
//         baby()
//     })
// })
xq()
    .then(marry)
    .then(baby)
// xq().then(marry)
// baby()

在这里我们可以理解为老王相亲的时候疯狂对相亲对象promise,才有了后面的步入婚姻的殿堂,结婚后想生个娃也要对妻子疯狂的promise,才有了后面的小王出生了。

老王长叹了一口气,终于通过promise挽回了形象。

小结

Js异步编程方法不只这两种,还有比如事件监听,发布/订阅,生成器函数 Generator/yield等。需要我们一起去探索研究,毕竟‘学无止境’。

转载自:https://juejin.cn/post/7225257817345884221
评论
请登录