promise、setTimeout 等异步 API 的执行顺序和区别。
promise 是 ES6 中用于异步编程的一种解决方案。它可以避免回调地狱的问题。setTimeout 是 JavaScript 中的定时函数,可以在一定的时间后执行代码。当 setTimeout 和 promise 同时使用时,它们两个的执行顺序如下:
1 | let promise = new Promise((resolve, reject) => { |
- promise 对象创建时处于 pending 状态。new Promise 里面的内容会立即执行
- setTimeout 会首先执行,因为它不需要等待任何操作,作为一个异步任务直接进入事件循环队列。
- promise 的 then/catch 被定义,但不会立即执行。
- 主线程继续执行同步代码,将 then/catch 放入微任务队列。
- 当主线程执行栈为空时,从微任务队列取出 then/catch 回调并执行。
- 同时 setTimeout 中的回调函数,会在计时结束后被放入宏任务队列。
- 主线程执行完所有微任务后,会检查宏任务队列,取出 setTimeout 回调函数并执行。
当 JS 引擎执行异步代码时,会按照这个顺序来执行:
主线程代码 > 微任务队列 > 宏任务队列
也就是:
- 首先执行主线程上的同步代码
- 主线程代码执行完毕后,检查微任务队列,依次执行所有微任务队列中的任务
- 微任务队列执行完毕后,检查宏任务队列,依次执行所有宏任务队列中的任务
- 然后循环往复上述过程
其中:
- 主线程代码指的是普通的同步代码
- 微任务包括 promise.then/catch、MutationObserver 等
- 宏任务包括 setTimeout、setInterval、DOM 事件回调、I/O 操作的回调(网络请求、文件读写、数据库操作)、postMessage/MessageChannel 的事件等
所以在异步代码中,我们往往先定义微任务,然后再定义宏任务。这样可以利用微任务的执行优先级,使得需要先执行的逻辑放在微任务中,从而控制代码的执行顺序。
执行结果答案
1 | 执行结果 |
习题,如果我们在 Promise 里面有 setTimeout,外面也有 setTimeout,那么谁会先执行,最终结果是什么呢
1 | setTimeout(() => { |