对于上述代码,我的理解:
.then
方法所属对象已经resolved
,所以 5 ~ 20 行代码进入微任务队列.then
方法所属对象pending
中,所以将 23 ~ 25 行加入第 5 行函数返回值对象的PromiseFulfill
属性中then
会将 15 ~ 17 行的代码加入第 9 行函数返回值对象的PromiseFulfill
属性中resolved
,因此将 23 ~ 25 行加入微任务队列实际输出和我上述的理解是一致的( 333 、555 、444 )。
按照 JavaScript 高级程序设计(第 4 版) 330 页的说法:
如果没有显式的返回语句,则 Promise.resolve()会包装默认的返回值 undefined 。
那实际上面例子中 5 ~ 20 行代码,是没有显式的返回语句的,按照我的理解,就相当于执行了 19 行的return Promise.resolve()
,但是当我取消注释 19 行后,输出结果变成了 333 、444 、555 。结果和我已经形成的理解不一致,想不太明白,还请各位老师帮助答疑解惑,万分感谢。
1
hiw2016 OP 文字版代码如下:
```js new Promise(resolve => { resolve(); }) .then( () => { new Promise((resolve) => { resolve(); }) .then( () => { console.log(333); } ) .then( () => { console.log(444); } ); // return Promise.resolve(); } ) .then( () => { console.log(555); } ); ``` |
2
hiw2016 OP new Promise(resolve => {
resolve(); }) .then( () => { new Promise((resolve) => { resolve(); }) .then( () => { console.log(333); } ) .then( () => { console.log(444); } ); // return Promise.resolve(); } ) .then( () => { console.log(555); } ); |
3
lmshl 2022-05-13 12:10:28 +08:00 2
|
4
shakukansp 2022-05-13 12:23:44 +08:00
你这返回了一个 promise.resolve(),后面的 555 被往后推了一步啊
|
5
thinkershare 2022-05-13 12:24:14 +08:00
因为规范并不保证 333, 444, 555 的执行顺序, 它唯一保证的就是 444 总应该在 333 后面, 而 333, 444, 555 的确定性顺序是未定义行为, 我猜想是编译器优化了无返回值的情况, 这样 555 就更快的得到了执行(还没有执行到 444, 当你手动编写了 Promise.resolve()后, 这个执行需要消耗时间, 这个期间, 444 的任务链条可能已经结束了执行, 因此就是你看到的 333, 444, 555, 不过正如楼上所说, 这些对实际开发影响很小. 你如果除了对 what, 还对 why 感兴趣, 也可以自己深入去研究一下
|
6
thinkershare 2022-05-13 12:26:44 +08:00
另外不要在携程中试图依赖确定性的调用顺序, 除非你手动同步, 或者使用链式等待
|
8
shakukansp 2022-05-13 12:43:08 +08:00 1
在 then 里面 return 233 和 return Promise.resolve(233)
promise 的规范是保证你在以下各 then(val)里拿到的 val 是 233 没说 return 233 和 return Promise.resolve(233) 是一样的 |
9
shakukansp 2022-05-13 12:43:59 +08:00
@shakukansp typo 了 保证你在下一个 then(val) 里拿到的 val 是 233
|
11
lmshl 2022-05-13 13:13:55 +08:00 1
@fulvaz 图是 Scala 的 Cats Effect 纤程库作者的 PPT
&t=426s 但 stackless coroutine 的本质概念都是一样的,而 stackful coroutine 和 stackless coroutine 又是理论上等价,可以转换的,很多语言都能同时支持这两种,比如 JS 的 Promise 和 async / await 。 所以这张图也是通用的,面向 blueprint 的设计方法也是通用的。 |
12
TWorldIsNButThis 2022-05-13 13:44:07 +08:00 via iPhone
我怎么觉得从语义上看 5 和 34 的顺序关系是无法保证的
除非是 return 第六行的 promise |
13
shakukansp 2022-05-13 14:03:31 +08:00 1
@TWorldIsNButThis
可以保证,如果 19 行注释掉那么假设 4 行的顺序为 1 ,6 行的 new Promise 也是 1 ,9 行和 22 行的 then 是 2 ,14 行 then 是 3 如果 19 行不注释,那么假设 4 顺序为 1 ,6 行 new Promise 是 1 ,依照规范,如果.then 中 return 的 x 是 promise 对象,那么当前 then 的状态变为此 promise 状态,所以当前的 then 必须等待 19 行的 Promise.resolve() 所以顺序变为 9 和 19 行是顺序 2 ,14 和 22 行为顺序 3 |
14
shakukansp 2022-05-13 14:26:49 +08:00
new Promise(resolve => {
resolve(); }) .then( () => { new Promise((resolve) => { resolve(); }) .then( () => { console.log(333); } ) .then( () => { console.log(444); } ); return Promise.resolve().then((val) => { console.log(233); }).then(() => { console.log(888); }).then(()=>{ console.log(999); }) } ) .then( () => { console.log(555); } ); 顺序: 333 和 233 平行,按照声明顺序 444 和 888 平行,按照声明顺序 接着 999 而 555 要等到上一个 then 中 return 的 promise 的最后一个 then resolve 所以 555 最后被输出 输出 333 233 444 888 999 555 楼主你自己再好好想想吧 |
15
rabbbit 2022-05-13 14:32:44 +08:00
别抠这个了,规范没定指不定哪天浏览器实现就变了.
来猜猜啥时候会输出 0-0. new Promise((r) => { console.log('in p0'); r(new Promise((r) => { console.log('in internal p'); r(); })); }) .then(() => { console.log('0-0') }) new Promise((r) => { console.log('in p1'); r(); }) .then(() => { console.log('1-0') }) .then(() => { console.log('1-1') }) .then(() => { console.log('1-2') }) .then(() => { console.log('1-3') }); |
16
rabbbit 2022-05-13 14:36:44 +08:00
再来猜猜这个,啥时候输出 0-0
let thenable = { then: function(resolve, reject) { console.log('in thenable'); resolve(42); } }; new Promise((r) => { console.log('in p0'); r(thenable); }) .then(() => { console.log('0-0') }) new Promise((r) => { console.log('in p1'); r(); }) .then(() => { console.log('1-0') }) .then(() => { console.log('1-1') }) .then(() => { console.log('1-2') }) .then(() => { console.log('1-3') }); |
17
rabbbit 2022-05-13 14:38:02 +08:00
面试的要是问你就把这个给他,看看他能不能答出来.
|
18
hiw2016 OP 感谢各位!@shakukansp 的代码很有直接帮助!以及其他老师们的回答也很有启发~
|
19
yugu9138 2022-05-13 22:05:24 +08:00 via iPhone
说实话,你这个代码语法写得是个灾难,
return Promise.resolve() 即可 不需要全部 null ,尽量写到一个流程内 而且这种多 promise 的,可以参考使用 async 更易明白流程结构 |
20
yugu9138 2022-05-13 22:11:39 +08:00 via iPhone
Promise.resolve().then(_=>{
return Promise.resolve("1111") }).then(c=>{ console.log(c) //11111 return Promise.resolve("2222") }).then(c=>{ console.log(c) //222 return Promise.resolve(c) }) Async: let _ = await Promise.resolve(); let c = await Promise.resolve("11111"); console.log(c); //1111 let j = await Promise.resolve("22222") console.log(j) //2222 |
21
celebrityii 2022-05-15 17:23:22 +08:00
返回 Promise.resolve() 后,只有当该 Promise 被解决之后,它之后的 then 语句才会被加入微任务队列
|