Promise 的then 里发生了什么

栏目: JavaScript · 发布时间: 5年前

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

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函数执行的返回值做一个操作!!!

需求:

  1. 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出去


以上所述就是小编给大家介绍的《Promise 的then 里发生了什么》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Blog Design Solutions

Blog Design Solutions

Richard Rutter、Andy Budd、Simon Collison、Chris J Davis、Michael Heilemann、Phil Sherry、David Powers、John Oxton / friendsofED / 2006-2-16 / USD 39.99

Blogging has moved rapidly from being a craze to become a core feature of the Internetfrom individuals sharing their thoughts with the world via online diaries, through fans talking about their favori......一起来看看 《Blog Design Solutions》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码