阅读 1212

一步步教你实现Promise/A+ 规范 完整版

前言

小伙伴们大家好,这里我将带大家手写Promise,作为前端的开发者,在日常的工作中,肯定避免不了一个问题,那就是异步编程

那么什么是异步编程?

cmd-markdown-logo

  • 从服务器获取数据,这个过程就叫做异步编程

  • 在node.js中去读取文件,这个过程也是异步的

关于异步的解决方案目前有四种:

  • callback(回调函数)
  • generato + co库
  • promise
  • async+await

今天就来为大家重要讲解一下promise,彻底的弄懂它

cmd-markdown-logo

学习之前

在学习之前我们需要弄懂以下的:

  • 了解Promise
  • es6 (主要是es6的箭头函数和es6的类)
  • this的指向问题
  • 关于promise/A+规范
参考文档:https://promisesaplus.com/
复制代码

术语

解决(fulfill)

指一个 promise 成功时进行的一系列操作,如状态的改变、回调的执行。虽然规范中用 fulfill 来表示解决,但在后世的 promise 实现多以 resolve 来指代之

拒绝(reject)

指一个 promise失败时进行的一系列操作

终值(eventual value)

所谓终值,指的是 promise 被解决时传递给解决回调的值,由于 promise 有一次性的特征,因此当这个值被传递时,标志着 promise 等待态的结束,故称之终值,有时也直接简称为值(value)

据因(reason)

也就是拒绝原因,指在 promise 被拒绝时传递给拒绝回调的值

Promise

promise是一个拥有then方法的对象或函数,其行为符合本规范

thenable

是一个定义了then方法的对象或函数

值(value)

指任何JavaScript的合法值(包括undefined、thenable和promise)

异常(exception)

是适用throw语句抛出的一个值

异步回调

回调地域

在需要多个操作的时候,会导致多个回调函数嵌套,导致代码不够直观,就是常说的回调地狱

并行结果

如果几个异步操作之间并没有前后顺序之分,但需要等多个异步操作都完成后才能执行后续的任务,无法实现并行节约时间

promise理解

Promise本意是承诺,在程序中的意思就是承诺我过一段时间后会给你一个结果:

什么时候会用到过一段时间?

是异步操作

异步是指可能比较长时间才有结果的才做,例如网络请求、读取本地文件等

Promise的状态

一个Promise的当前状态必须为以下三种状态中的一种

等待态(Pending)

处于等待态时,promise需满足以下条件:

可以迁移至执行态或拒绝态

执行态(Fulfilled)

处于执行态时,promise 需满足以下条件:

不能迁移至其他任何状态

必须拥有一个不可变的终值

拒绝态(Rejected)

处于拒绝态时,promise需要满足以下条件

不能迁移至其他任何状态

必须拥有一个不可变的据因

Then方法

一个promise必须提供一个then方法以访问其当前值、终值和据因

promise的then方法接受两个参数:

promise.then(onFulfilled, onRejected)
复制代码

onFulfilled 和 onRejected 都是可选参数

  • 如果onFullfilled不是函数,其必须被忽略
  • 如果onRejected不是函数,其必须被忽略

onFulfilled特性

如果onFulfilled是函数:

  • 当promise执行结束后其必须被调用,其第一个参数为promise的终值
  • 在promise执行结束前其不可被调用
  • 其调用次数不可超过一次

onRejected特性

如果onRejected是函数:

  • 当 promise 被拒绝执行后其必须被调用,其第一个参数为 promise 的据因
  • 在 promise 被拒绝执行前其不可被调用
  • 其调用次数不可超过一次

调用时机

onFulfilled 和 onRejected 只有在执行环境堆栈仅包含平台代码时才可被调用

调用要求

onFulfilled 和 onRejected 必须被作为函数调用(即没有 this 值)

多次调用

then 方法可以被同一个promise调用多次

  • 当 promise 成功执行时,所有 onFulfilled 需按照其注册顺序依次回调

  • 当 promise 被拒绝执行时,所有的 onRejected 需按照其注册顺序依次回调

返回

then 方法必须返回一个 promise 对象

准备

在开始之前我们需要创建三个文件

  • index.js进行原生的Promise演示
  • promise.js进行自定义的Promise演示
  • test.js是对promise.js进行测试

promise初体验

一个最基本的Promise长什么样?

代码如下:

index.js

new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(1)
    })
}).then(value => {
    console.log('value',value)
},reason => {
    console.log('reason',reason)
})
复制代码

运行结果:

value 1
复制代码

原生的promise它的参数不是函数的时,会发生什么?

代码如下:

index.js

new Promise(1)
复制代码

promise.js

class Promise {
    constructor(executor){
        //参数效验
        if(typeof executor !== 'function'){
            throw new TypeError('Promise resolver ${executor} is not a function')
        }
    }
}
复制代码

运行结果:

Promise resolver 1 is not a function
复制代码

这是一个最基本的promise

代码如下

index.js

new Promise((resolve, reject) => {
    console.log('早上好!')
        resolve(1)
})
复制代码

promise.js

class Promise {
    constructor(executor){
        //参数效验
        if(typeof executor !== 'function'){
            throw new TypeError('Promise resolver ${executor} is not a function')
        }
        const resolve = function (){

        }
        const reject = function (){

        }
        executor(resolve,reject)
    }
}
复制代码

运行结果:

早上好!
复制代码

再把上边代码测试一下

代码如下:

promise.js

class Promise {
  constructor(executor){
      //不能相信用户的输入,所以这里要做参数效验
      if(typeof executor !== 'function'){
          throw new TypeError('Promise resolver ${executor} is not a function')
      }
      //记录状态和值的改变
      //初始化值
      this.value = null //终值
      this.reason = null //拒因
      this.state = 'pending' //状态

      const resolve = value => {
          //成功后的一系列操作(状态的改变,成功回调的执行)
          if(this.state === 'pending'){
              //状态进行改变
              this.state = 'fulfilled'
              //执行成功的回调,把终值进行赋值
              this.value = value
          }
      }
      const reject = reason =>{
          //失败后的一系列操作(状态的改变,失败回调的执行)
          if(this.state === 'pending'){
              //状态进行改变
              this.state = 'rejected'
              //执行成功的回调,把据因进行赋值
              this.reason = reason
          }
      }
      executor(resolve,reject)
  }
}
module.exports = Promise
复制代码

test.js

const Promise = require('./promise.js')

new Promise((resolve, reject) => {
    console.log('早上好!')
        resolve(1)
})
复制代码

运行结果:

早上好!
复制代码

promise初步实现

把上边的代码进行优化

优化后的代码如下

promise.js

class Promise {
    constructor(executor){
        //不能相信用户的输入,所以这里要做参数效验
        if(typeof executor !== 'function'){
            throw new TypeError('Promise resolver ${executor} is not a function')
        }

        this.initValue()
        this.initBind()

        executor(this.resolve,this.reject)
    }
    //绑定 this
    initBind(){
        this.resolve = this.resolve.bind(this)
        this.reject = this.reject.bind(this)
    }
    //进行代码的优化
    initValue(){
        //记录状态和值的改变
        //初始化值
        this.value = null //终值
        this.reason = null //拒因
        this.state = 'pending' //状态
    }
    resolve(value){
        //成功后的一系列操作(状态的改变,成功回调的执行)
        if(this.state === 'pending'){
            //状态进行改变
            this.state = 'fulfilled'
            //执行成功的回调,把终值进行赋值
            this.value = value
        }
    }
    reject(reason){
        //失败后的一系列操作(状态的改变,失败回调的执行)
        if(this.state === 'pending'){
            //状态进行改变
            this.state = 'rejected'
            //执行成功的回调,把据因进行赋值
            this.reason = reason
        }
    }
    then() {}
}
module.exports = Promise
复制代码

test.js

const Promise = require('./promise.js')

new Promise((resolve, reject) => {
    console.log('早上好!')
        resolve(1)
})
复制代码

运行结果:

早上好!
复制代码

测试通过后,下一步then方法

代码如下:

promise.js

class Promise {
    constructor(executor){
        //不能相信用户的输入,所以这里要做参数效验
        if(typeof executor !== 'function'){
            throw new TypeError('Promise resolver ${executor} is not a function')
        }

        this.initValue()
        this.initBind()

        executor(this.resolve,this.reject)
    }
    //绑定 this
    initBind(){
        this.resolve = this.resolve.bind(this)
        this.reject = this.reject.bind(this)
    }
    //进行代码的优化
    initValue(){
        //记录状态和值的改变
        //初始化值
        this.value = null //终值
        this.reason = null //拒因
        this.state = 'pending' //状态
    }
    resolve(value){
        //成功后的一系列操作(状态的改变,成功回调的执行)
        if(this.state === 'pending'){
            //状态进行改变
            this.state = 'fulfilled'
            //执行成功的回调,把终值进行赋值
            this.value = value
        }
    }
    reject(reason){
        //失败后的一系列操作(状态的改变,失败回调的执行)
        if(this.state === 'pending'){
            //状态进行改变
            this.state = 'rejected'
            //执行成功的回调,把据因进行赋值
            this.reason = reason
        }
    }
    then(onFulfilled, onRejected) {
        //  参数效验
        if (typeof onFulfilled !== 'function'){
            onFulfilled = function(value) {
                return value
            }
        }
        if (typeof onRejected !== 'function'){
            onRejected = function(reason){
                throw reason
            }
        }
        if(this.state === 'fulfilled'){
            onFulfilled(this.value)
        }
        if(this.state === 'rejected'){
            onRejected(this.reason)
        }
    }
}
module.exports = Promise
复制代码

test.js

const Promise = require('./promise.js')

new Promise((resolve, reject) => {
    console.log('早上好!')
        resolve(1)
}).then(value=> {
    console.log('value',value)
},reason => {   
    console.log('reason',value)
})
复制代码

运行结果:

早上好!
value 1
复制代码

将上边的代码再次进行优化

代码如下:

promise.js

class Promise {
    constructor(executor){
        //不能相信用户的输入,所以这里要做参数效验
        if(typeof executor !== 'function'){
            throw new TypeError('Promise resolver ${executor} is not a function')
        }

        this.initValue()
        this.initBind()

        executor(this.resolve,this.reject)
    }
    //绑定 this
    initBind(){
        this.resolve = this.resolve.bind(this)
        this.reject = this.reject.bind(this)
    }
    //进行代码的优化
    initValue(){
        //记录状态和值的改变
        //初始化值
        this.value = null //终值
        this.reason = null //拒因
        this.state = Promise.PENDING //状态
    }
    resolve(value){
        //成功后的一系列操作(状态的改变,成功回调的执行)
        if(this.state === Promise.PENDING){
            //状态进行改变
            this.state = Promise.FULFILLED
            //执行成功的回调,把终值进行赋值
            this.value = value
        }
    }
    reject(reason){
        //失败后的一系列操作(状态的改变,失败回调的执行)
        if(this.state === 'pending'){
            //状态进行改变
            this.state = Promise.REJECTED
            //执行成功的回调,把据因进行赋值
            this.reason = reason
        }
    }
    then(onFulfilled, onRejected) {
        //  参数效验
        if (typeof onFulfilled !== 'function'){
            onFulfilled = function(value) {
                return value
            }
        }
        if (typeof onRejected !== 'function'){
            onRejected = function(reason){
                throw reason
            }
        }
        if(this.state === Promise.FULFILLED){
            onFulfilled(this.value)
        }
        if(this.state === Promise.REJECTED){
            onRejected(this.reason)
        }
    }
}
Promise.PENDING = 'pending'
Promise.FULFILLED = 'fulfilled'
Promise.REJECTED = 'reject'

module.exports = Promise
复制代码

test.js

const Promise = require('./promise.js')

new Promise((resolve, reject) => {
    console.log('早上好!')
        resolve(1)
}).then(value=> {
    console.log('value',value)
},reason => {   
    console.log('reason',value)
})
复制代码

运行结果:

早上好!
value 1
复制代码

异步解决实现

先看一下这段代码的执行顺序是什么样的?

代码如下

index.js

console.log('1')
new Promise((resolve, reject) => {
    console.log('2')
        resolve(1)
    }).then(value => {
    console.log('4')
    console.log('value',value)
},reason => {
    console.log('reason',reason)
})
console.log('3')
复制代码

运行结果:

1
2
3
4
value 1
复制代码

在测试文件运行这段代码执行顺序会是什么样?

代码如下

test.js

const Promise = require('./promise.js')

console.log('1')
new Promise((resolve, reject) => {
    console.log('2')
        resolve(1)
    }).then(value => {
    console.log('4')//立即执行了
    console.log('value',value)//立即执行了
},reason => {
    console.log('reason',reason)
})
console.log('3')
复制代码

运行结果:

1
2
4
value 1
3
复制代码

如何来模拟异步呢?

在promise.js里面添加代码,利用setTimeout,再运行test.js

代码如下:

promise.js

class Promise {
    constructor(executor){
        //不能相信用户的输入,所以这里要做参数效验
        if(typeof executor !== 'function'){
            throw new TypeError('Promise resolver ${executor} is not a function')
        }

        this.initValue()
        this.initBind()

        try{
            executor(this.resolve, this.reject)
        }catch(e){
            this.reject(e)
        }
    }
    //绑定 this
    initBind(){
        this.resolve = this.resolve.bind(this)
        this.reject = this.reject.bind(this)
    }
    //进行代码的优化
    initValue(){
        //记录状态和值的改变
        //初始化值
        this.value = null //终值
        this.reason = null //拒因
        this.state = Promise.PENDING //状态
    }
    resolve(value){
        //成功后的一系列操作(状态的改变,成功回调的执行)
        if(this.state === Promise.PENDING){
            //状态进行改变
            this.state = Promise.FULFILLED
            //执行成功的回调,把终值进行赋值
            this.value = value
        }
    }
    reject(reason){
        //失败后的一系列操作(状态的改变,失败回调的执行)
        if(this.state === 'pending'){
            //状态进行改变
            this.state = Promise.REJECTED
            //执行成功的回调,把据因进行赋值
            this.reason = reason
        }
    }
    then(onFulfilled, onRejected) {
        //  参数效验
        if (typeof onFulfilled !== 'function'){
            onFulfilled = function(value) {
                return value
            }
        }
        if (typeof onRejected !== 'function'){
            onRejected = function(reason){
                throw reason
            }
        }
        if(this.state === Promise.FULFILLED){
            setTimeout(() => {
                onFulfilled(this.value)
            })
        }
        if(this.state === Promise.REJECTED){
            setTimeout(() => {
                onRejected(this.reason)
            })
        }
    }
}
Promise.PENDING = 'pending'
Promise.FULFILLED = 'fulfilled'
Promise.REJECTED = 'reject'

module.exports = Promise
复制代码

test.js

const Promise = require('./promise.js')

console.log('1')
new Promise((resolve, reject) => {
    console.log('2')
        resolve(1)
    }).then(value => {
    console.log('4')
    console.log('value',value)
},reason => {
    console.log('reason',reason)
})
console.log('3')
复制代码

运行结果:

1
2
3
4
value 1
复制代码

假如在test.js里面抛出一个异常,会是怎么样?

代码如下:

test.js

const Promise = require('./promise.js')

console.log('1')
new Promise((resolve, reject) => {
    throw new Error('You write wrong')
    // console.log('2')
        resolve(1)
    }).then(value => {
    console.log('4')
    console.log('value',value)
},reason => {
    console.log('reason',reason)
})
console.log('3')
复制代码

运行结果:

1
3
reason Error: You write wrong
复制代码

上边是直接在最外层进行一个抛出

假设我们把代码放原生的promise里,会是怎样?

代码如下:

index.js

const Promise = require('./promise.js')

console.log('1')
new Promise((resolve, reject) => {
    throw new Error('You write wrong')
    // console.log('2')
        resolve(1)
    }).then(value => {
    console.log('4')
    console.log('value',value)
},reason => {
    console.log('reason',reason)
})
console.log('3')
复制代码

运行结果:

1
3
reason Error: You write wrong
复制代码

如果是test.js里是异步的会是什么样?

代码如下:

test.js

const Promise = require('./promise.js')

console.log('1')
new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('hello!')
            resolve(1)
        })
    }).then(value => {
    console.log('4')
    console.log('value',value)
},reason => {
    console.log('reason',reason)
})
console.log('3')
复制代码

运行结果:

1
3
hello!
复制代码

不知道大家有没有发现 '4' 没有执行,这是什么原因呢?

因为此时并没有直接进入setTimeout里面,而是进行了.then操作

cmd-markdown-logo
我们在promise.js里面看到,

此时.then操作的状态等于'pending',

它不等于'fulfilled',也不等于'reject',

所以它并没有执行这两个回调函数中的任意一个,所以.then方法并没有执行

cmd-markdown-logo
怎么解决这个问题呢?

首先在promise.js里面肯定要追加一个状态的判断

在promise.js里追加一个状态的判断

代码如下:

promise.js

class Promise {
    constructor(executor){
        //不能相信用户的输入,所以这里要做参数效验
        if(typeof executor !== 'function'){
            throw new TypeError('Promise resolver ${executor} is not a function')
        }

        this.initValue()
        this.initBind()

        try{
            executor(this.resolve, this.reject)
        }catch(e){
            this.reject(e)
        }
    }
    //绑定 this
    initBind(){
        this.resolve = this.resolve.bind(this)
        this.reject = this.reject.bind(this)
    }
    //进行代码的优化
    initValue(){
        //记录状态和值的改变
        //初始化值
        this.value = null //终值
        this.reason = null //拒因
        this.state = Promise.PENDING //状态
        this.onFulfilledCallbacks = []//成功回调
        this.onRejectedCallbacks = [] //失败回调
    }
    resolve(value){
        //成功后的一系列操作(状态的改变,成功回调的执行)
        if(this.state === Promise.PENDING){
            //状态进行改变
            this.state = Promise.FULFILLED
            //执行成功的回调,把终值进行赋值
            this.value = value
            //成功或者失败以后进行这两个数组的执行
            this.onFulfilledCallbacks.forEach((fn) => fn(this.value)
            )}
    }
    reject(reason){
        //失败后的一系列操作(状态的改变,失败回调的执行)
        if(this.state === 'pending'){
            //状态进行改变
            this.state = Promise.REJECTED
            //执行成功的回调,把据因进行赋值
            this.reason = reason
            this.onRejectedCallbacks.forEach(fn => fn(this.reason))
        }
    }
    then(onFulfilled, onRejected) {
        //  参数效验
        if (typeof onFulfilled !== 'function'){
            onFulfilled = function(value) {
                return value
            }
        }
        if (typeof onRejected !== 'function'){
            onRejected = function(reason){
                throw reason
            }
        }
        if(this.state === Promise.FULFILLED){
            setTimeout(() => {
                onFulfilled(this.value)
            })
        }
        if(this.state === Promise.REJECTED){
            setTimeout(() => {
                onRejected(this.reason)
            })
        }
        //在promise.js里面肯定要追加一个状态的判断
        if(this.state === Promise.PENDING){
            this.onFulfilledCallbacks.push((value) => {
                setTimeout(() => {
                    onFulfilled(value)
                })
            })
            this.onRejectedCallbacks.push((reason) => {
                setTimeout(() => {
                    onRejected(this.reason)
                })
            })
        }
    }
}
Promise.PENDING = 'pending'
Promise.FULFILLED = 'fulfilled'
Promise.REJECTED = 'reject'

module.exports = Promise
复制代码

运行结果:

1
3
hello!
4
value 1
复制代码

链式调用的简单解决方案

如何实现链式调用

代码如下:

index.js

new Promise((resolve, reject) => {
        resolve(1)
    })
    .then(
        value => {
         return 'good' + value
    },
    reason => {
        console.log('reason',reason)
    }
    )
    .then(
        value => {
        console.log('value',value)
    },
    reason => {
        console.log('reason',reason)
}
)
复制代码

运行结果:

value good1
复制代码

如何才能做到链式调用呢?

实现链式调用,且改变了后面的then的值,必须通过新的实例

代码如下:

index.js

new Promise((resolve, reject) => {
    // throw new Error('You write wrong')
    // console.log('2')
        resolve(1)
    })
    .then(
        value => {
         throw new Error('use')
         return 'good' + value
    },
    reason => {
        console.log('reason',reason)
    }
    )
    .then(
        value => {
        console.log('value',value)
    },
    reason => {
        console.log('reason',reason)
}
)
复制代码

promise.js

class Promise {
    constructor(executor){
        //不能相信用户的输入,所以这里要做参数效验
        if(typeof executor !== 'function'){
            throw new TypeError('Promise resolver ${executor} is not a function')
        }

        this.initValue()
        this.initBind()

        try{
            executor(this.resolve, this.reject)
        }catch(e){
            this.reject(e)
        }
    }
    //绑定 this
    initBind(){
        this.resolve = this.resolve.bind(this)
        this.reject = this.reject.bind(this)
    }
    //进行代码的优化
    initValue(){
        //记录状态和值的改变
        //初始化值
        this.value = null //终值
        this.reason = null //拒因
        this.state = Promise.PENDING //状态
        this.onFulfilledCallbacks = []//成功回调
        this.onRejectedCallbacks = [] //失败回调
    }
    resolve(value){
        //成功后的一系列操作(状态的改变,成功回调的执行)
        if(this.state === Promise.PENDING){
            //状态进行改变
            this.state = Promise.FULFILLED
            //执行成功的回调,把终值进行赋值
            this.value = value
            //成功或者失败以后进行这两个数组的执行
            this.onFulfilledCallbacks.forEach((fn) => fn(this.value)
            )}
    }
    reject(reason){
        //失败后的一系列操作(状态的改变,失败回调的执行)
        if(this.state === 'pending'){
            //状态进行改变
            this.state = Promise.REJECTED
            //执行成功的回调,把据因进行赋值
            this.reason = reason
            this.onRejectedCallbacks.forEach(fn => fn(this.reason))
        }
    }
    then(onFulfilled, onRejected) {
        //  参数效验
        if (typeof onFulfilled !== 'function'){
            onFulfilled = function(value) {
                return value
            }
        }
        if (typeof onRejected !== 'function'){
            onRejected = function(reason){
                throw reason
            }
        }
        // 实现链式调用,且改变了后面的then的值,必须通过新的实例
        let promise2 = new Promise((resolve, reject) => {
            if(this.state === Promise.FULFILLED){
                setTimeout(() => {
                    try{
                        const x = onFulfilled(this.value)
                        resolve(x)
                    }catch(e){
                        reject(e)
                    }
                })
            }
            if(this.state === Promise.REJECTED){
                setTimeout(() => {
                    try{
                        const x = onRejected(this.reason)
                        resolve(x)
                    }catch(e){
                        reject(e)
                    }
                })
            }
            //在promise.js里面肯定要追加一个状态的判断
            if(this.state === Promise.PENDING){
                this.onFulfilledCallbacks.push((value) => {
                    setTimeout(() => {
                        try{
                            const x = onFulfilled(value)
                            resolve(x)
                        }catch(e){
                            reject(e)
                        }
                    })
                })
                this.onRejectedCallbacks.push((reason) => {
                    setTimeout(() => {
                        try{
                            const x = onRejected(this.reason)
                            resolve(x)
                        }catch(e){
                            reject(e)
                        }
                    })
                })
            }
        })
        return promise2
    }
}
Promise.PENDING = 'pending'
Promise.FULFILLED = 'fulfilled'
Promise.REJECTED = 'reject'
Promise.resolvePromise = function(promise2, x, resolve, reject){}

module.exports = Promise
复制代码

运行结果:

reason Error: use
复制代码

链式调用的终极解决方案和测试

当返回值不是一个普通数值或一个基本类型,会是什么样情况?

代码如下:

test.js

const Promise = require('./promise.js')

new Promise((resolve, reject) => {
    // throw new Error('You write wrong')
    // console.log('2')
        resolve(1)
    })
    .then(
        value => {
         return new Promise((resolve) => {
             resolve(1)
         })
    },
    reason => {
        console.log('reason',reason)
    }
    )
    .then(
        value => {
        console.log('value',value)
    },
    reason => {
        console.log('reason',reason)
}
)
复制代码

运行结果:

value Promise {
  value: 1,
  reason: null,
  state: 'fulfilled',
  onFulfilledCallbacks: [],
  onRejectedCallbacks: [],
  resolve: [Function: bound resolve],
  reject: [Function: bound reject] }
复制代码

分析结果可知:

当x的值不是基本值的时候,而是promise实例得时候,必须等待这一个promise的时候结束,才能进行进一步执行

cmd-markdown-logo
所以规范提出了一个解决方案 针对resolvePromise的具体解决过程
cmd-markdown-logo
首先是判断promise2和x的值是否是相等的

如果相等的话,就抛出一个TypeError,是为了避免循环调用的问题

我们可以通过代码看一下:

index.js

let p1 = new Promise((resolve) => {
    resolve(1)
})
let p2 = p1.then(() => {
    return p2
})
复制代码

运行结果:

TypeError: Chaining cycle detected for promise
复制代码

链式调用出现了问题

在promise.js里面处理一下

promise.js

class Promise {
    constructor(executor) {
      // 参数校检
      if (typeof executor !== 'function') {
        throw new TypeError(`Promise resolver ${executor} is not a function`)
      }
  
      this.initValue()
      this.initBind()
  
      try {
        executor(this.resolve, this.reject)
      } catch (e) {
        this.reject(e)
      }
    }
  
    // 绑定 this
    initBind() {
      this.resolve = this.resolve.bind(this)
      this.reject = this.reject.bind(this)
    }
  
    // 初始化值
    initValue() {
      this.value = null // 终值
      this.reason = null // 拒因
      this.state = Promise.PENDING // 状态
      this.onFulfilledCallbacks = [] // 成功回调
      this.onRejectedCallbacks = [] // 失败回调
    }
  
    resolve(value) {
      // 成功后的一系列操作(状态的改变, 成功回调的执行)
      if (this.state === Promise.PENDING) {
        this.state = Promise.FULFILLED
        this.value = value
        this.onFulfilledCallbacks.forEach(fn => fn(this.value))
      }
    }
  
    reject(reason) {
      // 失败后的一系列操作(状态的改变, 失败回调的执行)
      if (this.state === 'pending') {
        this.state = Promise.REJECTED
        this.reason = reason
        this.onRejectedCallbacks.forEach(fn => fn(this.reason))
      }
    }
  
    then(onFulfilled, onRejected) {
      // 参数校检
      if (typeof onFulfilled !== 'function') {
        onFulfilled = function(value) {
          return value
        }
      }
  
      if (typeof onRejected !== 'function') {
        onRejected = function(reason) {
          throw reason
        }
      }
  
      // 实现链式调用, 且改变了后面then的值, 必须通过新的实例
      let promise2 = new Promise((resolve, reject) => {
        if (this.state === Promise.FULFILLED) {
          setTimeout(() => {
            try {
              const x = onFulfilled(this.value)
              Promise.resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          })
        }
  
        if (this.state === Promise.REJECTED) {
          setTimeout(() => {
            try {
              const x = onRejected(this.reason)
              Promise.resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          })
        }
  
        if (this.state === Promise.PENDING) {
          this.onFulfilledCallbacks.push(value => {
            setTimeout(() => {
              try {
                const x = onFulfilled(value)
                Promise.resolvePromise(promise2, x, resolve, reject)
              } catch (e) {
                reject(e)
              }
            })
          })
  
          this.onRejectedCallbacks.push(reason => {
            setTimeout(() => {
              try {
                const x = onRejected(this.reason)
                Promise.resolvePromise(promise2, x, resolve, reject)
              } catch (e) {
                reject(e)
              }
            })
          })
        }
      })
  
      return promise2
    }
  }
  
  Promise.PENDING = 'pending'
  Promise.FULFILLED = 'fulfilled'
  Promise.REJECTED = 'reject'
  Promise.resolvePromise = function(promise2, x, resolve, reject) {
    // x 与 promise 相等
    if (promise2 === x) {
      reject(new TypeError('Chaining cycle detected for promise'))
    }
  
    let called = false
    if (x instanceof Promise) {
      // 判断 x 为 Promise
      x.then(
        value => {
          Promise.resolvePromise(promise2, value, resolve, reject)
        },
        reason => {
          reject(reason)
        }
      )
    } else if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
      // x 为对象或函数
      try {
        const then = x.then
        if (typeof then === 'function') {
          then.call(
            x,
            value => {
              if (called) return
              called = true
              Promise.resolvePromise(promise2, value, resolve, reject)
            },
            reason => {
              if (called) return
              called = true
              reject(reason)
            }
          )
        } else {
          if (called) return
          called = true
          resolve(x)
        }
      } catch (e) {
        if (called) return
        called = true
        reject(e)
      }
    } else {
      resolve(x)
    }
  }
  
  module.exports = Promise
复制代码

test.js

const Promise = require('./promise.js')

new Promise((resolve, reject) => {
    // throw new Error('You write wrong')
    // console.log('2')
        resolve(1)
    })
    .then(
        value => {
         return new Promise((resolve) => {
             resolve(new Promise((resolve,reject) => {
                 resolve('333')
             })
             )
         })
    },
    reason => {
        console.log('reason',reason)
    }
    )
    .then(
        value => {
        console.log('then 2 value:',value)
    },
    reason => {
        console.log('reason',reason)
}
)
复制代码

运行结果:

then 2 value: 333
复制代码

cmd-markdown-logo

如何验证我们的promise是否正确

首先

我们需要安装一个promises-aplus-tests

npm install promises-aplus-tests
复制代码

用来测试自己的promise 符不符合promisesA+规范

然后

把下边这段代码copy到promise.js里面

Promise.defer = Promise.deferred = function () {
  let dfd = {}
  dfd.promise = new Promise((resolve,reject)=>{
    dfd.resolve = resolve;
    dfd.reject = reject;
  });
  return dfd;
}
module.exports = Promise;
复制代码

最后

执行输入npx promises-aplus-tests 你要测试的文件(promise.js)

npx promises-aplus-tests promise.js
复制代码

cmd-markdown-logo
以上,我们就完成了一个基于Promise A+规范的Promise

最后扩展

源码地址

https://github.com/shifengming/promise
复制代码

动手写一下

小伙伴们有兴趣可以手动写下面这些方法的实现

  • 手写Promise.all()
  • 手写Promise.race()
  • 手写Promise.prototype.catch

最后

如果本文对你有帮助得话,给本文点个赞❤️❤️❤️

欢迎大家加入,一起学习前端,共同进步!

cmd-markdown-logo
cmd-markdown-logo

关注下面的标签,发现更多相似文章
评论