解析Promise then回调返回值对执行顺序的影响
Promise then回调返回不同值时执行顺序差异的原因
var a = Promise.resolve(); var b = a.then(function a_then() { console.log(1); var c = Promise.resolve(); var d = c.then(function c_then() { console.log(2); }); var e = d.then(function d_then() { console.log(3); }); console.log(4); return c; // <--- 此处影响输出顺序 }); var f = b.then(function b_then() { console.log(5); var g = Promise.resolve(); 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);
当不添加return c(默认返回undefined)时,输出顺序为:9,1,4,2,5,8,3,6,7。
添加return c后,输出顺序变为:9,1,4,2,3,5,8,6,7。核心疑问是:为何3会在5和8之前输出?
执行流程详解(中文表格)
| 任务 | 操作 | a | b | c | d | e | f | g | h | i | PromiseJob队列 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 脚本执行 | a = Promise.resolve() | 已完成(F) | - | - | - | - | - | - | - | - | |
| 脚本执行 | b = a.then(a_then) | 已完成 | 等待中(?) | - | - | - | - | - | - | - | a_then |
| 脚本执行 | f = b.then(b_then) | 已完成 | 等待中 | - | - | - | 等待中(?) | - | - | - | a_then |
| 脚本执行 | console.log(9) | 已完成 | 等待中 | - | - | - | 等待中 | - | - | - | a_then |
| 宿主环境 | 取出a_then执行 | 已完成 | 等待中 | - | - | - | 等待中 | - | - | - | |
a_then | console.log(1) | 已完成 | 等待中 | - | - | - | 等待中 | - | - | - | |
a_then | c = Promise.resolve() | 已完成 | 等待中 | 已完成 | - | - | 等待中 | - | - | - | |
a_then | d = c.then(c_then) | 已完成 | 等待中 | 已完成 | 等待中 | - | 等待中 | - | - | - | c_then |
a_then | e = d.then(d_then) | 已完成 | 等待中 | 已完成 | 等待中 | 等待中 | 等待中 | - | - | - | c_then |
a_then | console.log(4) | 已完成 | 等待中 | 已完成 | 等待中 | 等待中 | 等待中 | - | - | - | c_then |
a_then | 返回c,将b置为已完成 | 已完成 | 已完成 | 已完成 | 等待中 | 等待中 | 等待中 | - | - | - | c_then |
| 宿主环境 | 取出c_then执行 | 已完成 | 已完成 | 已完成 | 等待中 | 等待中 | 等待中 | - | - | - | |
c_then | console.log(2) | 已完成 | 已完成 | 已完成 | 等待中 | 等待中 | 等待中 | - | - | - | |
c_then | 返回值将d置为已完成 | 已完成 | 已完成 | 已完成 | 已完成 | 等待中 | 等待中 | - | - | - | d_then |
| 宿主环境 | 取出d_then执行 | 已完成 | 已完成 | 已完成 | 已完成 | 等待中 | 等待中 | - | - | - | |
d_then | console.log(3) | 已完成 | 已完成 | 已完成 | 已完成 | 等待中 | 等待中 | - | - | - | |
d_then | 返回值将e置为已完成 | 已完成 | 已完成 | 已完成 | 已完成 | 已完成 | 等待中 | - | - | - | b_then |
| 宿主环境 | 取出b_then执行 | 已完成 | 已完成 | 已完成 | 已完成 | 已完成 | 等待中 | - | - | - | |
b_then | console.log(5) | 已完成 | 已完成 | 已完成 | 已完成 | 已完成 | 等待中 | - | - | - | |
b_then | g = Promise.resolve() | 已完成 | 已完成 | 已完成 | 已完成 | 已完成 | 等待中 | 已完成 | - | - | |
b_then | h = g.then(g_then) | 已完成 | 已完成 | 已完成 | 已完成 | 已完成 | 等待中 | 已完成 | 等待中 | - | g_then |
b_then | i = h.then(h_then) | 已完成 | 已完成 | 已完成 | 已完成 | 已完成 | 等待中 | 已完成 | 等待中 | 等待中 | g_then |
b_then | console.log(8) | 已完成 | 已完成 | 已完成 | 已完成 | 已完成 | 等待中 | 已完成 | 等待中 | 等待中 | g_then |
b_then | 返回值将f置为已完成 | 已完成 | 已完成 | 已完成 | 已完成 | 已完成 | 已完成 | 已完成 | 等待中 | 等待中 | g_then |
| 宿主环境 | 取出g_then执行 | 已完成 | 已完成 | 已完成 | 已完成 | 已完成 | 已完成 | 已完成 | 等待中 | 等待中 | |
g_then | console.log(6) | 已完成 | 已完成 | 已完成 | 已完成 | 已完成 | 已完成 | 已完成 | 等待中 | 等待中 | |
g_then | 返回值将h置为已完成 | 已完成 | 已完成 | 已完成 | 已完成 | 已完成 | 已完成 | 已完成 | 已完成 | 等待中 | h_then |
| 宿主环境 | 取出h_then执行 | 已完成 | 已完成 | 已完成 | 已完成 | 已完成 | 已完成 | 已完成 | 已完成 | 等待中 | |
h_then | console.log(7) | 已完成 | 已完成 | 已完成 | 已完成 | 已完成 | 已完成 | 已完成 | 已完成 | 等待中 | |
h_then | 返回值将i置为已完成 | 已完成 | 已完成 | 已完成 | 已完成 | 已完成 | 已完成 | 已完成 | 已完成 | 已完成 | |
| 宿主环境 | 任务队列清空 | 已完成 | 已完成 | 已完成 | 已完成 | 已完成 | 已完成 | 已完成 | 已完成 | 已完成 |
核心差异原因
关键在于**a_then返回c时,b的完成时机被延迟**:
- 当
a_then返回undefined时,b会立即进入已完成状态,b_then被直接加入PromiseJob队列,排在c_then之后。所以c_then输出2后,会先执行b_then输出5和8,再执行d_then输出3。 - 当
a_then返回c(一个已完成的Promise)时,根据Promise规则:当前Promise(b)会等待返回的Promise完成后才进入完成状态。虽然c已经是完成状态,但这个等待逻辑会被安排到下一轮微任务中,b_then不会立即入队,必须等c相关的后续微任务(c_then、d_then)全部执行完毕后,才会将b_then加入队列。
因此c_then输出2并将d置为完成后,d_then会被优先执行输出3,之后才轮到b_then输出5和8。
返回d或e时逻辑类似:b会等待d/e完成,而d/e的完成依赖于前面的微任务,所以b_then的执行会被进一步延迟,直到相关微任务全部执行完毕。
内容的提问来源于Stack Exchange,提问作者Won Jin Kim
相关产品推荐
相关产品推荐

