Promise 的then 里发生了什么

40次阅读

共计 6023 个字符,预计需要花费 16 分钟才能阅读完成。

Promise 大家都知道怎么用,但是对于内部的原理很多人都不是很清楚
来看一个面试题:Promise 的 then 是怎么实现的
首先来分析一下 then

then 是属于实例上的方法
参数有 2 个,分别为 onFulfilled, onRejected,并且都是可选的
可以实现链式调用
then 执行要执行 Promise onFulfilled 或者 onRejected 方法
参数 onFulfilled,onRejected 分别有自己的参数,分别是 resolve 的参数跟 reject 的参数
then 只能使用前一个 then 的返回值
then 返回值不能是同一个 promise

来一个一个看吧
then 是属于实例上的方法
Promise.prototype.then = function(){}
参数有 2 个,分别为 onFulfilled, onRejected,并且都是可选的
Promise.prototype.then = function(onFulfilled,onRejected){}
可以实现链式调用
Promise.prototype.then = function(onFulfilled,onRejected){
return new Promise(function(resolve,reject){
// 代码省略
})
}
要实现 promise 链式 返回的必须是一个 Promise 再由于 status 改变状态了不能再变 所以需要第二个 …. 第 N 个 promise 调用新的 resolve
then 执行要执行 Promise onFulfilled 或者 onRejected 方法
Promise.prototype.then = function(onFulfilled,onRejected){
var self = this; // 保存当前上下文
return new Promise(function(resolve,reject){
if(self.status === ‘resolved’){
onFulfilled(self.res)
}
if(self.status === ‘rejected’){
onRejected(self.err)
}
})
}
Promise.resolve(res)、、同步代码的执行情况下 上述逻辑成立
参数 onFulfilled,onRejected 分别有自己的参数,分别是 resolve 的参数跟 reject 的参数
res 跟 err 来源如下
function Promise(executor){
let self = this
this.status = ‘pending’ // Promise 的状态值
this.res = undefined // 存成功之后的值
this.err = undefined // 存失败之后的值
executor(resolve,reject)
// 省略代码
}
executor 有 2 个参数 分别为 resolve,reject 当调用
new Promise((resolve,reject)=>{
setTimeout(()=>{
if(true){
resolve(‘res’)
}else{
reject(‘err’)
}

},1000)
})
executor 会执行 并且把成功的值或者失败的值抛出来,resolve 跟 reject 也是 2 个函数 定义在 Promise 内部
function Promise(executor){
// … 代码省略
function resolve(res){
self.res = res
}
function reject(err){
self.err = err
}
executor(resolve,reject)

}
resolve 跟 reject 还需要改变 Promise 的状态值 并且一旦发生改变之后不能再次改变
function Promise(executor){
// 代码省略
function resolve(res){
if(self.status === ‘pending’){
self.status = ‘resolved’
self.res = res
}
}
function reject(err){
if(self.status === ‘pending’){
self.status = ‘rejected’
self.err = err
}
}
executor(resolve,reject)
}
我们在 executor 中操作的往往是异步代码,这个之后直接 then,status 的状态值并未发生改变,所以不会执行 onFulfilled 跟 onRejected, 这个时候我们需要订阅
function Promise(executor){
// 代码省略
this.onFulfilledCallback = [] // 存成功之后的回调
this.onrejectedCallback = [] // 存失败之后的回调
function resolve(res){
if(self.status === ‘pending’){
self.status = ‘resolved’
self.res = res
}
}
function reject(err){
if(self.status === ‘pending’){
self.status = ‘rejected’
self.err = err
}
}
executor(resolve,reject)
}

new Promise(executor).then((onFulfilled,onrejected)=>{
var self = this; // 保存当前上下文 ** 注意:第二次调用 then this 是指向 new Promise 的 **
return new Promise(function(resolve,reject){
// … 代码省略
if(self.status === ‘pending’){
self.onFulfilledCallback.push(function(){// 把成功之后需要做的事存起来 有多少个 then 就有多少个函数
onFulfilled(self.res) // 注意 这里的 self.res 会随着 then 的调用发生改变 因为每次 then 都 new 了一个 Promise
})
self.onrejectedCallback.push(function(){// 把失败之后的事存起来 有多少个 then 就有多少个函数
onFulfilled(self.err)
})
}
})
})
当 resolve 的时候 或者 reject 的时候 一一拿出来执行
function resolve(res){
// 省略代码
self.res = res
onFulfilledCallback.forEach(fn=>{
fn()
})
}
实现链式调用我们需要对 then 函数执行的返回值做一个操作!!!
需求:
then 的参数 onFulfilled 函数 需要接收到上一个 onfulfilled 的执行结果
Promise.prototype.then = function(onFulfilled,onRejected){
return new Promise((resolve,reject)=>{
// … 代码省略
if(self.status === ‘resolved’){
let x = onFulfilled(self.res) // 拿到 onFulfilled 的执行结果 注意:这里执行 Promise.resolve() 同步代码
// 然后把 x 传递给下一个 then
resolve(x)
}
})
}
如果 resolve 是异步的话
Promise.prototype.then = function(onFulfilled,onRejected){
return new Promise((resolve,reject)=>{
// … 代码省略
if(self.status === ‘resolved’){
let x = onFulfilled(self.res) // 拿到 onFulfilled 的执行结果 注意:这里执行的是 Promise.resolve() 同步代码
// 然后把 x 传递给下一个 then
resolve(x)
}
if(self.status === ‘pending’){
self.onFulfilledCallback.push(function(){
let x = onFulfilled(self.res) // 这里的 self.res 是上一个 new Promise 上的值 此时的 onFUlfilled 相当于 fn(){let x = onFulfilled}
resolve(x)
})
}
})
}

// 同时为了拿到 fn(){let x = onFulfilled …} 得值 传递给下一个 onFulfilled,onFulfilledCallback 需要改写 onRejectedCallback 同理

onFulfilledCallback.forEach(fn=>{
return fn()
})
onRejectedCallback.forEach(fn=>{
return fn()
})

onFullfillled 的返回值可能是个 promise 也可能是个普通值
考虑如下情况:
1. 返回值是个 promise
Promsie.resolve(11).then(res=>{

return new Promise(executor)
})
2. 返回值是个普通值
Promsie.resolve(11).then(res=>{

return 1
})
最关键的来了我们需要对 onFullfillled 的返回值进行判断
修改 then 函数
// … 代码省略
if(self.status === ‘resolved’){
let x = onFulfilled(self.res) // 拿到 onFulfilled 的执行结果 注意:这里执行的是 Promise.resolve() 同步代码
// 然后把 x 传递给下一个 then
promiseResolve(x,resolve,reject)

}
function promiseResolve(x,resolve,reject){
if((typeof x != null && typeof x == ‘object’) || typeof x == ‘function’){// 确定是个引用类型
if (x instanceof Promise){// 确定是个 Promise 实例
let then = x.then
then.call(x,res=>{ // 保证 x 调用 res=>{}, res=>{} 是 then 的 onFulfilled 方法 一直判断直到 onFulfilled 返回的是个普通值 然后 resolve 出去
promiseResolve(res,resolve,reject)
},err=>{
reject(err)
})
}
}else{
resolve(x)
}
}
then 返回值不能是同一个 promise
考虑这样极端问题:
let p = new Promise(resolve=>{
resolve(3333)
})

let p2 = p.then(resolve=>{
return p2
})
这样会出现什么问题呢,onFulfilled 的返回结果如果是一个 promise 上面的递归调用已经说明了 我们要不断的去递归最终的 onFulfilled 结果 然后再改变 p2 的 status 这样变成了 p2 去等待 p2 的执行结果 函数死掉了
所以 onFulfilled 的返回结果需要跟 then 的返回结果去比较 修改函数
function promiseResolve(x,resolve,reject,promise){
if(x === promise){
return new Error(‘ 引用错误!’)
}
if((typeof x != null && typeof x == ‘object’) || typeof x == ‘function’){// 确定是个引用类型
if (x instanceof Promise){// 确定是个 Promise 实例
let then = x.then
then.call(x,res=>{ // 保证 x 调用 res=>{}, res=>{} 是 then 的 onFulfilled 方法 一直判断直到 onFulfilled 返回的是个普通值 然后 resolve 出去
promiseResolve(res,resolve,reject)
},err=>{
reject(err)
})
}
}else{
resolve(x)
}
}

Promise.prototype.then = function(onFulfilled,onRejected){

// … 代码省略
let promise2 = new Promise((resolve,reject)=>{
if(self.status === ‘pending’){
self.onFulfilledCallback.push(function(){
let x = onFulfilled(self.res) // 这里的 self.res 是上一个 new Promise 上的值 此时的 onFUlfilled 相当于 fn(){let x = onFulfilled}
promiseResolve(x,resolve,reject,promise2)
})
}
})
return promise2
}
这段代码还是有错误,由于 javascript 主线程是单线程的,所以在 then 里的 promiseResolve 是拿不到 promise2 的 所以我们需要开启异步 使用定时器或者 nextTick 这里我们用定时器
Promise.prototype.then = function(onFulfilled,onRejected){

// … 代码省略
let promise2 = new Promise((resolve,reject)=>{
if(self.status === ‘pending’){
self.onFulfilledCallback.push(function(){
setTimeout(()=>{
let x = onFulfilled(self.res) // 这里的 self.res 是上一个 new Promise 上的值 此时的 onFUlfilled 相当于 fn(){let x = onFulfilled}
promiseResolve(x,resolve,reject,promise2)
},0)
})
}
})
return promise2
}
此时 整个 then 差不多完成了
需要补充的点

onFulfilled,onRejected 有可能是 undefined 这里未做判断
在 new Promise 时 executor 函数内部有可能报错 这里未使用 try catch 捕获
这里对于 onRejected 的错误未抛出

总结
then 每次创建一个新的 promise 对象 对于同步的 resolve,reject 直接调用 onFulfilled 或者 onRejected,对于异步的 resolve,reject 使用订阅发布模式,把每个 resolve,reject 暂存起来 等达到条件时候一一执行,并且拿到返回结果去跟内部的 promise 比较,并且判断如果是一个 promise 的话,不断解析 onFulfilled 的返回结果 直至 resolve 出去

正文完
 0