手把手带你实现符合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+规范)