手把手带你实现符合Promise/A+规范的Promise

Posted c.

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手把手带你实现符合Promise/A+规范的Promise相关的知识,希望对你有一定的参考价值。

文章目录

手把手带你实现符合Promise/A+规范的Promise

如果你还没有使用Promise的经验,那我建议你先阅读博主之前关于《带你快速入门ES6中的Promise对象》的文章。

本篇博文主要带领大家一步步实现Promise/A+规范的Promise,所以前提是你需要对Promise有一定的基础,然后通过一步步实现自己的Promise,才能更加深入的了解到Promise底层的实现原理。

什么是Promise/A+规范?

首先什么是Promise/A+规范?官网上是这么描述的:

An open standard for sound, interoperable javascript promises—by implementers, for implementers.

简而言之它是一套标准,ES中标准的Promise实现就是遵守这一套标准的。当然还有其他的Promise库,他们也是遵守这一套标准的。所以这套标准告诉我们一个规范的Promise需要满足哪些条件。具体的条件可以在官网中获取,这里就不多赘述了。官网链接:https://promisesaplus.com/

一步步实现自定义Promise

我们自己实现的自定义Promise首先肯定是符合Promise/A+规范的,然后我们会参考ES6的Promise进行对比来一步步实现自己的Promise。

构造函数

首先我们来看规范中怎么定义一个Promise

“promise” is an object or function with a then method whose behavior conforms to this specification.

然后我们来看,ES6中是怎么创建一个Promise的?

let promise = new Promise(function(resolve, reject) 
  /*
    如果操作成功,调用resolve并传入value   --> resolve(value)
    如果操作失败,调用reject并传入reason   --> rejectreason)
  */
)

可以看到ES6中是通过new一个Promise,然后传入一个方法,方法有两个参数(resolve, reject),当异步操作成功,调用resolve,异步操作失败调用reject。 所以这两个参数实际上是两个方法,并确实带一个参数的方法。

然后在定义中还说了,一个promise是需要带一个then方法的,我们可以看看ES6中的then方法

这个then方法接受两个参数:

promise.then(onFulfilled, onRejected)

接下来我们可以模仿着,写一下我们自定义Promise的构造函数then方法

function Promise(executor) //构造函数传入一个方法,方法有两个参数,两个参数都是一个方法并且带一个传入参数



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


resolve 与 reject的构建与基础实现

如果你有一定的基础,你就知道Promise是有三种状态的pending, fulfilled, rejected

可以看规范中对Promise states的定义:

A promise must be in one of three states: pending, fulfilled, or rejected.

  • 2.1.1. When pending, a promise:
    • 2.1.1.1. may transition to either the fulfilled or rejected state.
  • 2.1.2. When fulfilled, a promise:
    • 2.1.2.1. must not transition to any other state.
    • 2.1.2.2. must have a value, which must not change.
  • 2.1.3. When rejected, a promise:
    • 2.1.3.1. must not transition to any other state.
    • 2.1.3.2. must have a reason, which must not change.

然后我们看到ES6中的Promise对象上是会有一个PromiseState的属性的,这个属性只有三种状态pending, fulfilled, rejected

而且当一个Promise实例化出来的时候,状态是pending,然后通过调用resolve方法或者reject方法去改变Promise的状态。

所以我们需要我们自定义的Promise加一个属性,然后默认初始化的时候是pending

function Promise(executor)  //构造函数传入一个方法,方法有两个参数,两个参数都是一个方法并且带一个传入参数
    //添加属性
    this.PromiseState = 'pending';


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


然后我们继续来完善一些细节,首先我们看到ES6的Promise中还有一个属性就是PromiseResult,他是用来存储这个Promise的值,默认是undefined

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function Promise(executor)  //构造函数传入一个方法,方法有两个参数,两个参数都是一个方法并且带一个传入参数
    //添加属性
    this.PromiseState = PENDING
    this.PromiseResult = undefined


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


然后还有几个细节就是,Promise在实例化完成之后就会立马执行,比如ES6中的Promise

所以我们在Promise的构造函数中需要调用,传进来的executor方法。但是这个executor方法还有两个参数resolve,reject,我们需要给他传进去,所以我们还需要定义一下这两个方法。

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function Promise(executor)  //构造函数传入一个方法,方法有两个参数,两个参数都是一个方法并且带一个传入参数
    //添加属性
    this.PromiseState = PENDING
    this.PromiseResult = undefined

    //resolve 函数
    function resolve(data) 
       
    
    //reject 函数
    function reject(data) 
       
    

    //同步调用『执行器函数』
    executor(resolve, reject)



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


接下来,我们需要去实现resolve方法和reject方法。

首先我们知道在ES6的Promise中,可以通过resolve方法把Promise从pending变成fulfilled; 可以通过通过reject方法把Promise从pending变成rejected, 并且传入方法的参数,最后会变成Promise的PromiseResult属性的值。如下:

所以接下来我们就可以实现resolve方法和reject方法了。

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function Promise(executor)  //构造函数传入一个方法,方法有两个参数,两个参数都是一个方法并且带一个传入参数
    //添加属性
    this.PromiseState = PENDING
    this.PromiseResult = undefined
    //保存实例对象的 this 的值
    //此处可以不写,但是下面function方法需要改为箭头函数,否则function默认指向是window
    const self = this
    //resolve 函数
    function resolve(data) 
        //1. 修改对象的状态 (promiseState)
        self.PromiseState = FULFILLED; // fulfilled
        //2. 设置对象结果值 (promiseResult)
        self.PromiseResult = data;
    
    //reject 函数
    function reject(data) 
        //1. 修改对象的状态 (promiseState)
        self.PromiseState = REJECTED; // rejected
        //2. 设置对象结果值 (promiseResult)
        self.PromiseResult = data;
    

    //同步调用『执行器函数』
    executor(resolve, reject)



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


接下来我们可以测试一下实例化Promise对象的时候有没有问题了,我们写这样的一段测试代码,来看看控制台的输出。

 let p1 = new Promise((resolve, reject) => 
    console.log("同步执行1")
    resolve('success')
)
let p2 = new Promise((resolve, reject) => 
    console.log("同步执行2")
    reject('failed')
)
console.log("同步执行3")
console.log(p1)
console.log(p2)

throw 抛出异常改变状态

我们在学习promise的时候也知道,当在promise中的异步操作发生异常的时候,最后的状态会变成rejected,而且最后的PromiseResult的值,是我们抛出的这个异常对象。但我们现在的代码很明显没有这一步的操作。

下面是ES6中Promise的执行结果:

接下来我们就来处理这一步,所以我们需要捕获executor(resolve, reject)执行的异常,然后把当前的状态变成rejected

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function Promise(executor)  //构造函数传入一个方法,方法有两个参数,两个参数都是一个方法并且带一个传入参数
    //添加属性
    this.PromiseState = PENDING
    this.PromiseResult = undefined
    //保存实例对象的 this 的值
    //此处可以不写,但是下面function方法需要改为箭头函数,否则function默认指向是window
    const self = this
    //resolve 函数
    function resolve(data) 
        //1. 修改对象的状态 (promiseState)
        self.PromiseState = FULFILLED; // fulfilled
        //2. 设置对象结果值 (promiseResult)
        self.PromiseResult = data;
    
    //reject 函数
    function reject(data) 
        //1. 修改对象的状态 (promiseState)
        self.PromiseState = REJECTED; // rejected
        //2. 设置对象结果值 (promiseResult)
        self.PromiseResult = data;
    

    try 
        //同步调用『执行器函数』
        executor(resolve, reject)
     catch (error) 
        //修改 promise 对象状态为『失败』
        reject(error)
    




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


接下来我们来测试这一部分的功能

let p3 = new Promise((resolve, reject) => 
    throw new Error('error!');
)

控制台输出如下:

Promise的状态一旦改变,就不会再变

一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected

比如在ES6中执行如下代码,可以看出来,一旦Promise的状态改变了,就不会在变了

但是我们现在的实现中没有做这样的限制,所以现在我们需要加上这个逻辑,也就是只有在pending状态下,才能变为fulfilled或者rejected

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function Promise(executor)  //构造函数传入一个方法,方法有两个参数,两个参数都是一个方法并且带一个传入参数
    //添加属性
    this.PromiseState = PENDING
    this.PromiseResult = undefined
    //保存实例对象的 this 的值
    //此处可以不写,但是下面function方法需要改为箭头函数,否则function默认指向是window
    const self = this
    //resolve 函数
    function resolve(data) 
        if (self.PromiseState !== PENDING)    //判断状态,保证promise的状态只能从'pending'变为'fulfilled'
            return;
        
        //1. 修改对象的状态 (promiseState)
        self.PromiseState = FULFILLED; // fulfilled
        //2. 设置对象结果值 (promiseResult)
        self.PromiseResult = data;
    
    //reject 函数
    function reject(data) 
        //判断状态
        if (self.PromiseState !== PENDING)  //判断状态,保证promise的状态只能从'pending'变为'rejected'
            return;
        
        //1. 修改对象的状态 (promiseState)
        self.PromiseState = REJECTED; // rejected
        //2. 设置对象结果值 (promiseResult)
        self.PromiseResult = data;
    

    try 
        //同步调用『执行器函数』
        executor(resolve, reject)
     catch (error) 
        //修改 promise 对象状态为『失败』
        reject(error)
    




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


then 方法执行回调基础实现

promise的then(onFulfilled, onRejected)方法当promise的状态是fulfilled的时候就会调用onFulfilled方法,当状态是rejected的时候就会调用onRejected方法,并且会把PromiseResult的结果传入。

所以下面我们需要实现then方法

Promise.prototype.then = function (onFulfilled, onRejected) 
    //调用回调函数  PromiseState
    if (this.PromiseState === FULFILLED) 
        onFulfilled(this.PromiseResult)
    
    if (this.PromiseState === REJECTED) 
        onRejected(this.PromiseResult)
    

我们可以进行测试看看

let p1 = new Promise((resolve, reject) => 
    resolve('success')
)
p1.then(function (resolved)  
    console.log(resolved)
, function (rejected)  
    console.log(rejected)
)

let p2 = new Promise((resolve, reject) => 
    reject('failed')
)
p2.then(function (resolved)  
    console.log(resolved)
, function (rejected)  
    console.log(rejected)
)

但上面的方法存在一个问题,就是当promise里面的操作是异步执行之后才进行resolve方法或者reject方法的话,在执行then就会有问题,因为这个时候PromiseState的状态还是pending

我们可以测试看看

let p1 = new Promise((resolve, reject) => 
    setTimeout(() => resolve('success'), 0) // resolve方法会异步执行,所以会先执行下面的then方法,此时当前的状态是pending
)
p1.then(function (resolved)  //then方法执行,判断当前状态,因为当前状态是pending,所以不会进行任何操作
    console.log(resolved)
, function (rejected) 
    console.log(rejected)
)

结果就是控制台没有输出

但是如果是在ES6下,输出的结果是什么呢,我们可以看看是能够正常输出的

所以我们下面需要来解决一下这个异步的问题

异步任务 then 方法实现

我们现在的问题就是,如果promise里面是同步改变promise的状态,那么后面在进行then方法的时候就能够拿到promise改变的状态;但是如果promise里面是异步改变的话,就需要等promise改变状态之后,才会执行then的回调

那么怎么实现,等promise的状态发生改变之后,才会执行then的回调呢?

其实也就是当我们在执行resolve方法或者reject方法的时候,我们需要去调用then的回调。那很显然我们就需要保存then的回调,不然当then方法执行完之后,我们就没法再次获取到回调的内容。所以我们在then方法中就需要把我们的回调保存起来。

所以我们需要在promise中新增一个属性,用来存储then方法的回调,然后在then方法执行的时候,如果当前的状态是pending的话,就把回调保存起来,当以后状态从pending变成其他状态的时候,就可以把回调方法拿出来执行了。

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function Promise(executor)  //构造函数传入一个方法,方法有两个参数,两个参数都是一个方法并且带一个传入参数
    //添加属性
    this.PromiseState = PENDING
    this.PromiseResult = undefined
    this.callback = ;	 //保存then方法的回调
    //保存实例对象的 this 的值
    //此处可以不写,但是下面function方法需要改为箭头函数,否则function默认指向是window
    const self = this
    //resolve 函数
    function resolve(data) 
        if (self.PromiseState !== PENDING)    //判断状态,保证promise的状态只能从'pending'变为'fulfilled'
            return;
        
        //1. 修改对象的状态 (promiseState)
        self.PromiseState = FULFILLED; // fulfilled
        //2. 设置对象结果值 (promiseResult)
        self.PromiseResult = data;
        //调用成功的回调函数  加判断的原因是防止无回调报错
        if (self.callback.onFulfilled) 
            self.callback.onFulfilled(self.PromiseResult);
        
    
    //reject 函数
    function reject(data) 
        //判断状态
        if (self.PromiseState !== PENDING)  //判断状态,保证promise的状态只能从'pending'变为'rejected'
            return;
        
        //1. 修改对象的状态 (promiseState)
        self.PromiseState = REJECTED; // rejected
        //2. 设置对象结果值 (promiseResult)
        self.PromiseResult = data;
        //调用失败的回调函数  加判断的原因是防止无回调报错
        if (self.callback.onRejected) 
            self.callback.onRejected(self.PromiseResult);
        
    

    try 
        //同步调用『执行器函数』
        executor(resolve, reject)
     catch (error) 
        //修改 promise 对象状态为『失败』
        reject(error)
    




Promise.prototype.then = function (onFulfilled, onRejected) 
    // 当promise的状态为pending的时候就把回调方法保存起来,等待状态改变的时候进行调用
    if (this.PromiseState === PENDING) 
        this.callback = 
            onFulfilled,
            onRejected
        
    
    //如果状态是成功或者失败的话,就直接执行回调方法
    if (this.PromiseState === FULFILLED) 
        onFulfilled(this.PromiseResult)
    
    if (this.PromiseState === REJECTED) 
        onRejected(this.PromiseResult)
    

然后我们在进行之前的测试,就可以发现能够正常输出了

根据Promise/A+规范 优化then方法

在规范中,对then方法有下面这一段描述

  • 2.2.1. Both onFulfilled and onRejected are optional arguments:
    • 2.2.1.1. If onFulfilled is not a function, it must be ignored.
    • 2.2.1.2. If onRejected is not a function, it must be ignored.
  • 2.2.2. If onFulfilled is a function:
    • 2.2.2.1. it must be called after promise is fulfilled, with promise’s value as its first argument.
    • 2.2.2.2. it must not be called before promise is fulfilled.
    • 2.2.2.3. it must not be called more than once.
  • 2.2.3. If onRejected is a function,
    • 2.2.3.1. it must be called after promise is rejected, with promise’s reason as its first argument.
    • 2.2.3.2. it must not be called before promise is rejected.
    • 2.2.3.3. it must not be called more than once.

上面的大部分我们已经实现了,但是还是有一些细节还没有实现,关于2.2.1这部分规范我们需要优化一下

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function Promise(executor)  //构造函数传入一个方法,方法有两个参数,两个参数都是一个方法并且带一个传入参数
    //添加属性
    this.PromiseState = PENDING
    this.PromiseResult = undefined
    this.callback = ;	 //保存then方法的回调
    //保存实例对象的 this 的值
    //此处可以不写,但是下面function方法需要改为箭头函数,否则function默认指向是window
    const self = this
    //resolve 函数
    function resolve(data) 
        if (self.PromiseState !== PENDING)    //判断状态,保证promise的状态只能从'pending'变为'fulfilled'
            return;
        
        //1. 修改对象的状态 (promiseState)
        self.PromiseState = FULFILLED; // fulfilled
        //2. 设置对象结果值 (promiseResult)
        self.PromiseResult = data;
        //调用成功的回调函数  加判断的原因是防止无回调报错
        if (self.callback.onFulfilled) 
            self.callback.onFulfilled(self.PromiseResult);
        
    
    //reject 函数
    function reject(data) 
        //判断状态
        if (self.PromiseState !== PENDING)  //判断状态,保证promise的状态只能从'pending'变为'rejected'
            return;
        
        //1. 修改对象的状态 (promiseState)
        self.PromiseState = REJECTED; // rejected
        Promise的源码实现(完美符合Promise/A+规范)

Promise的源码实现(完美符合Promise/A+规范)

手写 Promise

深入理解Promise并写一个符合Promise a+规范的Promise代码

手写Promise

硬核调试实操 | 手把手带你实现 Serverless 断点调试