Js中异步代码挂起怎么解决?
从下面代码引入问题
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()
结果为
???这是不是有点违背了道德,只能说老王是个渣男。这时候我们就需要使用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等。需要我们一起去探索研究,毕竟‘学无止境’。
来源:juejin.cn/post/7225257817345884221