
本文深入探讨 javascript 中 promise 异步函数的执行机制,特别是 `then` 方法如何与微任务队列(promisejob queue)协同工作。通过一个具体代码示例,我们将逐步解析代码执行流程、promise 状态变化以及回调函数入队与出队的时机,揭示 `console.log` 输出顺序背后的原理,帮助开发者掌握 promise 异步行为的精确控制。
JavaScript 异步编程基础与 Promise 机制
JavaScript 是一种单线程语言,这意味着它一次只能执行一个任务。为了处理耗时的操作(如网络请求、文件读写),JavaScript 引入了异步编程机制。Promise 是 ES6 引入的一种处理异步操作的模式,它代表一个异步操作的最终完成(或失败)及其结果值。
理解 Promise 的执行顺序,关键在于把握 JavaScript 的事件循环(Event Loop)、调用栈(Call Stack)、微任务队列(Microtask Queue,也称 PromiseJob Queue)和宏任务队列(Macrotask Queue)的概念。
调用栈 (Call Stack):同步代码执行的地方。当函数被调用时,它被推入栈中;函数返回时,它被弹出。微任务队列 (Microtask Queue):存放 Promise 的 then/catch/finally 回调函数等。微任务在当前宏任务执行完毕后,且在下一个宏任务开始前,会被清空。宏任务队列 (Macrotask Queue):存放如 setTimeout、setInterval、I/O 操作等回调。在清空微任务队列后,事件循环会从宏任务队列中取出一个任务执行。
Promise 的核心特性:
Promise.resolve():创建一个已解决(fulfilled)的 Promise,这个操作本身是同步的。then() 方法:当一个 Promise 被解决(fulfilled)时,其 .then() 中注册的回调函数会被放入微任务队列。then() 方法本身总是返回一个新的 Promise,这个新 Promise 的状态最初是 pending。它的解决或拒绝取决于 then 回调函数的执行结果。即使 then() 是在一个已解决的 Promise 上调用的,它返回的 Promise 仍然是 pending,因为其回调需要异步执行。
示例代码分析
为了更清晰地演示 Promise 的执行顺序,我们使用一个带有命名 Promise 和回调函数的示例代码:
立即学习“Java免费学习笔记(深入)”;
var a = Promise.resolve(); // Promise a 立即解决var b = a.then(function a_then() { console.log(1); var c = Promise.resolve(); // Promise c 立即解决 var d = c.then(function c_then() { console.log(2); }); var e = d.then(function d_then() { console.log(3); }); console.log(4);});var f = b.then(function b_then() { console.log(5); var g = Promise.resolve(); // Promise g 立即解决 var h = g.then(function g_then() { console.log(6); }); var i = h.then(function h_then() { console.log(7); }); console.log(8);});console.log(9);
我们将逐行分析这段代码的执行流程,并观察 console.log 的输出顺序。
详细执行流程解析
以下是代码执行的详细步骤,关注调用栈、Promise 状态和微任务队列的变化:
阶段一:同步代码执行
var a = Promise.resolve();
a 被创建并立即进入 fulfilled 状态。a 的状态: fulfilled。微任务队列: 空。
var b = a.then(function a_then() { … });
a.then() 被调用。由于 a 已经 fulfilled,a_then 回调函数被添加到微任务队列。b 被创建,状态为 pending。a 的状态: fulfilled;b 的状态: pending。微任务队列: [a_then]。
var f = b.then(function b_then() { … });
b.then() 被调用。由于 b 仍是 pending 状态,b_then 回调函数在 b 解决后才会被添加。f 被创建,状态为 pending。a 的状态: fulfilled;b 的状态: pending;f 的状态: pending。微任务队列: [a_then]。
console.log(9);
同步代码执行,输出 9。输出: 9微任务队列: [a_then]。
至此,所有同步代码执行完毕。调用栈清空,事件循环开始检查微任务队列。
阶段二:第一次微任务队列处理 (a_then)
事件循环从微任务队列中取出 a_then 并执行。
微任务队列: 空。
console.log(1); (在 a_then 内部)
输出 1。输出: 9, 1
var c = Promise.resolve();
c 被创建并立即进入 fulfilled 状态。c 的状态: fulfilled。
var d = c.then(function c_then() { … });
c.then() 被调用。由于 c 已经 fulfilled,c_then 回调函数被添加到微任务队列。d 被创建,状态为 pending。微任务队列: [c_then]。
var e = d.then(function d_then() { … });
d.then() 被调用。由于 d 仍是 pending 状态,d_then 回调函数在 d 解决后才会被添加。e 被创建,状态为 pending。微任务队列: [c_then]。
console.log(4); (在 a_then 内部)
输出 4。输出: 9, 1, 4
a_then 函数执行完毕,其返回值(undefined)用于解决 b。
b 的状态变为 fulfilled。由于 b 已经 fulfilled,之前注册在 b.then() 上的 b_then 回调函数现在被添加到微任务队列。微任务队列: [c_then, b_then]。
阶段三:第二次微任务队列处理 (c_then)
事件循环从微任务队列中取出 c_then 并执行。
微任务队列: [b_then]。
console.log(2); (在 c_then 内部)
输出 2。输出: 9, 1, 4, 2
c_then 函数执行完毕,其返回值(undefined)用于解决 d。
d 的状态变为 fulfilled。由于 d 已经 fulfilled,之前注册在 d.then() 上的 d_then 回调函数现在被添加到微任务队列。微任务队列: [b_then, d_then]。
阶段四:第三次微任务队列处理 (b_then)
事件循环从微任务队列中取出 b_then 并执行。
微任务队列: [d_then]。
console.log(5); (在 b_then 内部)
输出 5。输出: 9, 1, 4, 2, 5
var g = Promise.resolve();
g 被创建并立即进入 fulfilled 状态。g 的状态: fulfilled。
var h = g.then(function g_then() { … });
g.then() 被调用。由于 g 已经 fulfilled,g_then 回调函数被添加到微任务队列。h 被创建,状态为 pending。微任务队列: [d_then, g_then]。
var i = h.then(function h_then() { … });
h.then() 被调用。由于 h 仍是 pending 状态,h_then 回调函数在 h 解决后才会被添加。i 被创建,状态为 pending。微任务队列: [d_then, g_then]。
console.log(8); (在 b_then 内部)
输出 8。输出: 9, 1, 4, 2, 5, 8
b_then 函数执行完毕,其返回值(undefined)用于解决 f。
f 的状态变为 fulfilled。微任务队列: [d_then, g_then]。
阶段五:第四次微任务队列处理 (d_then)
事件循环从微任务队列中取出 d_then 并执行。
微任务队列: [g_then]。
console.log(3); (在 d_then 内部)
输出 3。输出: 9, 1, 4, 2, 5, 8, 3
d_then 函数执行完毕,其返回值(undefined)用于解决 e。
e 的状态变为 fulfilled。微任务队列: [g_then]。
阶段六:第五次微任务队列处理 (g_then)
事件循环从微任务队列中取出 g_then 并执行。
微任务队列: 空。
console.log(6); (在 g_then 内部)
输出 6。输出: 9, 1, 4, 2, 5, 8, 3, 6
g_then 函数执行完毕,其返回值(undefined)用于解决 h。
h 的状态变为 fulfilled。由于 h 已经 fulfilled,之前注册在 h.then() 上的 h_then 回调函数现在被添加到微任务队列。微任务队列: [h_then]。
阶段七:第六次微任务队列处理 (h_then)
事件循环从微任务队列中取出 h_then 并执行。
微任务队列: 空。
console.log(7); (在 h_then 内部)
输出 7。输出: 9, 1, 4, 2, 5, 8, 3, 6, 7
h_then 函数执行完毕,其返回值(undefined)用于解决 i。
i 的状态变为 fulfilled。微任务队列: 空。
最终输出顺序为:9, 1, 4, 2, 5, 8, 3, 6, 7。
注意事项与总结
同步优先原则:JavaScript 总是优先执行所有同步代码。只有当调用栈清空后,事件循环才会检查微任务队列。微任务队列的优先级:微任务队列在每个宏任务(包括主脚本的执行)结束后立即清空。这意味着在一个宏任务内部产生的微任务,会在当前宏任务结束,但下一个宏任务开始之前被执行。then() 返回的新 Promise 总是 pending:理解 then() 方法返回的 Promise 总是从 pending 状态开始非常重要。它的最终状态取决于其回调函数的执行结果,而回调函数是异步执行的。Promise 链式调用:当一个 Promise 解决后,其 .then() 回调会被加入微任务队列。如果这个回调内部又返回了一个 Promise,那么下一个 .then() 的回调会等待这个内部 Promise 解决后才会被加入微任务队列。Promise.resolve() 的同步性:Promise.resolve() 本身是同步的,它会立即创建一个已解决的 Promise。但是,对这个 Promise 调用 .then() 所注册的回调,仍然是异步的,会被放入微任务队列。
通过以上详细的步骤分析,我们可以清晰地看到 JavaScript Promise 异步机制的内部工作原理,特别是微任务队列在调度 Promise 回调中的核心作用。掌握这些概念对于编写健壮、可预测的异步 JavaScript 代码至关重要。
以上就是深入理解 JavaScript Promise 异步执行顺序与微任务队列的详细内容,更多请关注创想鸟其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1539917.html
微信扫一扫
支付宝扫一扫