1.1什么叫异步
异步是相对于同步而言的,很好理解。
同步就是一件事一件事的执行。只有前一个任务执行完毕,才能执行后一个任务。而异步比如:
setTimeout(() => {
console.log('what is 异步')
},1000)
setTimeout就是一个异步任务,当js引擎顺序执行到的时候发现它是个异步任务,则会把这个任务放进任务队列中去,继续执行后面的代码。所以简单来说只要改变的js的执行顺序,就是异步操作。
1.2为什么要在js中使用异步
由于javascript是单线程的,只能在js引擎的主线程上运行,所以js只能一行一行的执行,不能再同一时间执行多个js任务,这就会导致如果有一段耗时较长的计算,或者是一个ajax请求,如果没有异步,就会出现用户长时间等待的情况。
1.3常见的异步模式
- 回调函数
- 事件监听
- 发布/订阅模式(又称观察者模式)
- promise
1.4 JS如何实现异步
具体JS是如何异步操作呢?
就是JS的事件循环机制。
具体来说:
当JS解析执行时,会被引擎分为两类任务,同步任务(synchronous)和异步任务(asynchronous)。
对于同步任务来说,会被推到执行栈按顺序去执行这些任务。
对于异步任务来说,当其可以执行时,会被放到一个任务队列(task queue)里等待js引擎去执行。
当执行栈中的所有同步任务完成后,js引擎才会去任务队列里查看是否有任务存在,并将任务放到执行栈中去执行,执行完了又会去任务队列里查看是否有已经可执行的任务。这种循环检查的机制,就叫做事件循环(event loop)。
对于任务队列,其实是有更细的分类。其被分为微任务(microtask)队列 && 宏任务(macrotask)队列。
- 宏任务:setTimeout,setInterval等,会被放到宏任务队列
- 微任务:Promise的then,Mutation Observer等,会被放在微任务队列
Event Loop的执行顺序是:
- 首先执行栈里的任务
- 执行栈清空之后,检查微任务(microtask)队列,将可执行的微任务全部执行
- 取宏任务(macrotask)队列中的第一项执行。
- 回到第二步。
注意:微任务队列每次全部执行,宏任务队列每次只取一次执行。
举个例子:
setTimeout(() => {
console.log('我是第一个宏任务');
Promise.resolve().then(() => {
console.log('我是第一个宏任务里的第一个微任务');
});
Promise.resolve().then(() => {
console.log('我是第一个宏任务里的第二个微任务');
});
},0)
setTimeout(() => {
console.log('我是第二个宏任务');
},0);
Promise.resolve().then(() => {
console.log('我是第一个微任务');
});
console.log('执行同步任务');
最后的执行结果是:
- // 执行同步任务
- // 我是第一个微任务
- // 我是第一个宏任务
- // 我是第一个宏任务里的第一个微任务
- // 我是第一个宏任务里的第二个微任务
- // 我是第二个宏任务