promise 及 setTimeout 执行顺序
setTimeout(function() { console.log(1); }, 0); new Promise(function(res, rej) { res(2); console.log(0); }).then(console.log); console.log(3);
执行顺序如下:
setTimeout 的任务会被排到队列尾部,同步任务执行结束后立即执行 setTimeout(即 console.log(1));
而 promise 一旦建立,其中的任务就会立即执行(即 console.log(0));
最外层的 console.log(3) 为同步任务,则按顺序执行;
promise 中的 then 会等待 resolve 执行结束后即执行(即 res(2));
所以执行后打印出来的顺序为:0、3、2、undefined、1
微任务与宏任务
console.log('script start') setTimeout(function() { console.log('setTimeout') }, 0) new Promise(resolve => { console.log('Promise') resolve() }) .then(function() { console.log('promise1') }) .then(function() { console.log('promise2') }) console.log('script end') // script start => Promise => script end => promise1 => promise2 => setTimeout
以上代码虽然 setTimeout
写在 Promise
之前,但是因为 Promise
属于微任务而 setTimeout
属于宏任务,所以会有以上的打印。
微任务包括 process.nextTick
,promise
,Object.observe
,MutationObserver
宏任务包括 script
, setTimeout
,setInterval
,setImmediate
,I/O
,UI rendering
很多人有个误区,认为微任务快于宏任务,其实是错误的。因为宏任务中包括了 script
,浏览器会先执行一个宏任务,接下来有异步代码的话就先执行微任务。
所以正确的一次 Event loop 顺序是这样的
- 执行同步代码,这属于宏任务
- 执行栈为空,查询是否有微任务需要执行
- 执行所有微任务
- 必要的话渲染 UI
- 然后开始下一轮 Event loop,执行宏任务中的异步代码
通过上述的 Event loop 顺序可知,如果宏任务中的异步代码有大量的计算并且需要操作 DOM 的话,为了更快的 界面响应,我们可以把操作 DOM 放入微任务中