博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Promise 的then 里发生了什么
阅读量:7104 次
发布时间:2019-06-28

本文共 7842 字,大约阅读时间需要 26 分钟。

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

转载地址:http://wzuhl.baihongyu.com/

你可能感兴趣的文章
ubuntu下eclipse及Gedit中文乱码问题解决,配置GB18030编码
查看>>
JavaScript:九种弹出对话框
查看>>
OpenCASCADE Expression Interpreter by Flex & Bison
查看>>
使用swagger2配置springboot时出现的问题
查看>>
麦兜的志愿
查看>>
一个问题提交的实例(js原生动画,原生ajax,js引用加参数)
查看>>
请不要遗忘了学习这种感觉
查看>>
看大数据平台如何打造餐饮业务一体化?
查看>>
linux软raid练习
查看>>
Java在WEB项目中获取文件路径
查看>>
QT打印
查看>>
zz 说说iOS的多线程Core Data
查看>>
个人总结的 Qt 安装教程(转载)
查看>>
android music开发笔记
查看>>
vSphere5.5体系结构
查看>>
mysql中的文件排序(filesort)
查看>>
nopcommerce 商城案例
查看>>
不使用其他变量实现两个变量交换
查看>>
Jmeter两种录制方法
查看>>
前尘浮华一场梦 NOI2018 游记
查看>>