下面的内容建议了解事件循环后再看效果更好
开胃小知识点(不懂没关系,往下看):
new Promise((resolve, reject) => {
console.log("fun2")
}).then((data)=>{
console.log(data)
console.log("await堵塞")
})
// 结果为:fun2
因为一直没有改变状态所以外层new Promise一直是padding状态,所以then不会执行,new Promise内不会自动resolve,而then方法可以返回一个 新 的 Promise 实例,注意是 新的实例,与 Promise 链条传递的 之前的 Promise 实例 并无多大关系,而且这个新的实例 状态 一定不是 pending,这是then 可以链式调用的原因。如下代码
new Promise((resolve, reject) => {
console.log("fun2")
resolve(1)
}).then((data)=>{
console.log(data)
console.log("await堵塞")
}).then(()=>{
console.log('then')
})
// 结果为:fun2,1,await堵塞,then
Promise.then
then的返回值
首先,then 可以链式调用的原因是因为 then 方法默认会返回一个 新 的 Promise 实例,注意是 新的实例,与 Promise 链条传递的 之前的 Promise 实例 并无多大关系,而且这个新的实例 状态 一定不是 pending
then返回值分类
- 不
return值(等同于return undefined)/显示地return非promise的值 - 显示
return具有resolve的promise的值
then对不同类型返回值的处理
- 处理return
非promise的值
Promise.resolve().then(() => {});
上面代码then中相当于return undefined,在被抛出返回值的时候,会被Promise.resolve包装,所以最后的promise状态为fulfilled,这个包装的过程不发生在return时候,而发生在整体代码执行完 抛出 返回值的过程 被包装。也就是说你 不能理解 成这样:
Promise.resolve().then(() => { return Promise.resolve(undefined) })
- 处理return
具有resolve的promise的值
Promise1.resolve().then(() => Promise2.resolve())
这里的过程很复杂,分为三步:
- 执行 ResolvePromise (来源于 v8 引擎内部),调用 promiseResolveThenableJobTask (来源于 v8 引擎内部) 产生一个微任务放置到微任务队列,这个微任务就是一个函数
let promiseResolveThenableJobTask = () => {p1.then((value) => { ReslovePromise(p2, value) // 传递 promise
})}
- 在主线程任务清空的情况下,取出并执行 promiseResolveThenableJobTask 微任务,执行完这个任务也就是调用这个函数,会产生一个 Promise2.fulfilled 的微任务,这个微任务作用是将内部的 Promise2 的结果传递给 then 外部
- 主线程从微任务队列取出执行 Promise2.fulfilled 任务,调用 ReslovePromise 函数 ,然后用 Promise.resolve 包装返回结果
所以,按照执行的顺序,在时间顺序上完全可以理解为插入了两个 你感受不到的微任务,1.promiseResolveThenableJobTask微任务;2.Promise2.fulfilled微任务(Promise2指的是里面的promise实例)
接下来我们来看一道经典例题
Promise.resolve() // p1
.then(() => {
console.log(0); // then0
return Promise.resolve(); // p0
}).then(() => {
console.log(4); // then4
});
Promise.resolve() // p2
.then(() => {
console.log(1); // then1
}).then(() => {
console.log(2); // then2
}).then(() => {
console.log(3); // then3
}).then(() => {
console.log(5); // then5
}).then(() => {
console.log(6); // then6
});// 0 1 2 3 4 5 6
如代码中注释中所说注释return这一行,相当于第一种:return非promise的返回值;2.不注释这一行,相当于第二种:return具有resolve的promise的返回值
注释情况我们不解释:结果一定是交替执行,不懂的小伙伴可以看我的上一篇文章nodejs事件循环
我们根据上面学习的知识来解释一下不注释return行时,返回具有resolve的promise返回值的执行即上面代码的执行顺序。
- 执行完所有同步代码, p1 和 p2 的状态变为 fulfilled,并且微任务队列中插入两个处理后续的then的微任务(放入微任务队列的是then里面的回调,nodejs事件循环 有提过),微任务队列变成 【then0,then1】(then根据里面的打印值命名)
- 从微任务中取出 then0 ,
输出0,看到返回值是promise类型,调用NewPromiseResolveThenableJobTask产生一个微任务,其实这个微任务我们可以理解为在Promise.resolve()中自动加了一个then,then就是微任务,此时微任务队列变成 【then1,NewPromiseResolveThenableJobTask(p1.fulfilled)】 - 从微任务队列中取出 then1 并执行,
输出 1,调用下面的then2,微任务队列变成 【NewPromiseResolveThenableJobTask(p1.fulfilled),then2】 - 从微任务队列中取出 NewPromiseResolveThenableJobTask(p1.fulfilled) 并执行,然后向微任务队列添加微任务p0.fulfilled(p0指里面返回的Promise.resolve())这里就是传值到外层,这是一个异步任务(作用:将 p0 传递到then外部),微任务队列变为 【then2, p0.fulfilled】
- 从微任务队列中取出 then2 并执行,
输出 2,将下一个then3放入微任务队列 ,微任务队列变成 [p0.fulfilled, then3] - 从微任务队列中取出 p0.fulfilled 并执行,将 p0 传递给传出去,将then4放入微任务队列,微任务队列变成 【then3,then4】
- 从微任务队列中取出 then3 并执行,
输出 3,将下一个then5放入微任务队列,微任务队列变成 【then4,then5】 - 从微任务队列中取出 then4 并执行,
输出 4,微任务队列变成 【then5】 - 从微任务队列中取出 then5 并执行,
输出 5,将下一个then6放入微任务队列,微任务队列变成 【then6】 - 从微任务队列中取出 then6 并执行,
输出 6,执行完毕
如果你不想了解的那么细致,你直接将代码按照时间顺序将 return promise 替换为两个 then,因为这些时间执行了两个处理 promise 的 微任务
替换代码如下
Promise.resolve() // p1
.then(() => {
console.log(0); // then0
return Promise.resolve()
.then(() => {
console.log('隐藏'); // 这里是隐藏的第一个微任--then
}); // 隐藏的第二个微任务--向外传值
}).then(() => {
console.log(4); // then4
});
Promise.resolve() // p2
.then(() => {
console.log(1); // then1
}).then(() => {
console.log(2); // then2
}).then(() => {
console.log(3); // then3
}).then(() => {
console.log(5); // then5
}).then(() => {
console.log(6); // then6
});// 0 1 2 3 4 5 6
变形
如果我们将 promise 链条后面的 then 拿进来呢?
Promise.resolve().then(() => {
console.log(0);
return Promise.resolve().then(() => {
console.log(4);
}).then(() => {
console.log(7);
}).then(() => {
console.log(8);
});
});
Promise.resolve().then(() => {
console.log(1);
}).then(() => {
console.log(2);
}).then(() => {
console.log(3);
}).then(() => {
console.log(5);
}).then(() => {
console.log(6);
});// 0 1 4 2 7 3 8 5 6
其实这个代码用我们上一篇文章nodejs事件循环就可以解释了,将Promise.resolve()作为同步代码,你会看到交替执行的结果.其实它是执行完内部的 promise 链条后,才会执行 promiseResolveThenableJobTask 后面的过程
总结
promise.then 显示指定类型为 promise 作为返回值,会引起 js 解析引擎 额外的处理过程,多了两个时序,可以将这两个时序理解为,加了一个then和向外传值,这样理解让这个过程变得更加简单,当然如果then中返回promise实例,且promise实例后面又跟一个then,则就多一个实例,即向外传递值的过程。
思考(不懂可忽略,几乎不会有这样的代码)
我们思考一个问题,如果中间有几个then没有呢?我说的是new promise嵌套的情况,代码如下:
// 实验代码
new Promise(res => {//8
res(new Promise(res => {//7
res(new Promise(res => {//6
res(new Promise(res => {//5
res(new Promise(res => {//4
res(new Promise(res => {//3
res(new Promise(res => {//2
res(new Promise(res => { //1
res('fun2 微任务')
}).then((data) => {
console.log(data, 'then1')
return data
})
)
}).then((data) => {
console.log(data, 'then2')
return data
})
)
}).then((data) => {
console.log(data, 'then3')
return data
})
)
})
// .then((data) => {
// console.log(data, 444)
// return data
// })
)
})
// .then((data)=>{
// console.log(data, 555)
// return data
// })
)
})
.then((data) => {
console.log(data, 666)
return data
})
)
})
.then((data) => {
console.log(data, 777)
return data
})
)
})
// 参考代码
Promise.resolve().then(() => {
console.log(1);
}).then(() => {
console.log(2);
}).then(() => {
console.log(3);
}).then(() => {
console.log(4);
}).then(() => {
console.log(5);
}).then(() => {
console.log(6);
}).then(() => {
console.log(7);
}).then(() => {
console.log(8);
}).then(() => {
console.log(9);
}).then(() => {
console.log(10);
}).then(() => {
console.log(11);
}).then(() => {
console.log(12);
})
自己总结的结论(仅供参考)
上面的实验代码
- 如果每个then都没有缺失,“fun2 微任务 666”后面应该是“11”
- 如果缺失了“fun2 微任务 555”,则“fun2 微任务 666”后面应该是“10”
- 如果缺失了“fun2 微任务 555”和“fun2 微任务 444”,则“fun2 微任务 666”后面应该是“9”
- 如果缺失了“fun2 微任务 555”和“fun2 微任务 444”和“fun2 微任务 333”,则“fun2 微任务 666”后面应该是“8”
- 我查微任务的方法是,先将所有的都加then,再各自加一个向外传递值的微任务,如果是连续多个then没有的情况,向外传值的微任务合并为一个