近期学习了promise A+规范,想做个记录并分享给有需要的兄弟姐妹,废话不多说,能搜索到这个的肯定已经知道promise是什么东西了,我们直接开始。
Promise A+ 规范
英文好的小伙伴可以直接看原文档 首先我们先来了解Promise的相关术语,以便后面我们提到大家知道其对应的概念是什么。
术语
- promise 是一个有then方法的对象或者函数,其行为遵循本规范。
- thenable 是一个定义了 then 方法的对象或函数
- value 是promise状态成功时的值,也就是resolved的参数,包括各种数据类型。(包括 undefined , thenable 和 promise)
- reason 是promise状态失败时的值,也就是reject的参数,表示拒绝的原因。
- exception 是一个使用throw抛出的异常值。
规范
下面我们就开始看看PromiseA+规范。
promise 状态
promise必须处于以下三种状态中的一种Pending,Fulfilled,Rejected。
- Pending
-
初始的状态,可以迁移至执行态或拒绝态。 -
一个proimse在resolve或者reject前都处于这个状态。
- Fulfilled
-
最终态,不能迁移至其他任何状态。 -
必须拥有一个不可变的终值value。 -
promise被resolve后会变成这个状态。
- Rejected
-
最终态,不能迁移至其他任何状态。 -
必须拥有一个不可变的拒因reason。 -
promise被reject后会变成这个状态。
then
一个 promise 必须提供一个 then 方法以访问其当前值(value)、终值和据因(reason)。
//promise 的 then 方法接受两个参数
promise.then(onFulfilled,onRejected)
- 参数要求
- then的两个参数都为可选参数
- 如果 onFulfilled 不是函数,其必须被忽略
- 如果 onRejected 不是函数,其必须被忽略
- onFulfilled 特性
- 在promise变成fulfilled时,应该调用onFulfilled,参数为value。
- 在promise变成fulfilled之前,不应该被调用。
- 只能被调用一次。
- onRejected 特性
- 在promise变成rejected时,应该调用onRejected,参数为reason。
- 在promise变成rejected之前,不应该被调用。
- 只能被调用一次。
- 调用时机
- onFulfilled 和 onRejected 只有在执行环境堆栈仅包含平台代码时才可被调用(onFulfilled 和 onRejected应该为微任务)
- then方法可以被调用多次
- 当 promise 成功执行时,所有 onFulfilled 需按照其注册顺序依次回调。
- 当 promise 被拒绝执行时,所有的 onRejected 需按照其注册顺序依次回调。
- 返回值
- then应该返回一个promise
promise2 = promise1.then(onFulfilled,onRejected)
- onFulfilled 或 onRejected 执行的结果为x,调用则运行下面的 Promise 解决过程:[[Resolve]](promise2, x) 注:这个解决过程我们用一个resolvePromise方法来写。
- 如果onFulfilled不是一个函数,promise2 以 promise1的value 触发fulfilled
- 如果onRejected 不是一个函数,promise2 以 promise1的reason触发rejected 注:理解上面的“返回”部分非常重要,即:不论 promise1 被 reject 还是被 resolve 时 promise2 都会被 resolve,只有出现异常时才会被 rejected。
- resolvePromise
resolvePromise(promise2, x, resolve, reject)
- 如果 promise2 和 x 相等,那么 reject TypeError
- 如果 x 是一个 promsie 如果x是pending态,那么promise必须要在pending,直到 x 变成 fulfilled or rejected. 如果 x 被 fulfilled, fulfill promise with the same value. 如果 x 被 rejected, reject promise with the same reason.
- 如果 x 是一个 object 或者 是一个 function
let then = x.then.
如果 x.then 这步出错,那么 reject promise with e as the reason. 如果 then 是一个函数,then.call(x, resolvePromiseFn, rejectPromise)
resolvePromiseFn 的 入参是 y, 执行 resolvePromise(promise2, y, resolve, reject);
rejectPromise 的 入参是 r, reject promise with r.
如果 resolvePromise 和 rejectPromise 都调用了,那么第一个调用优先,后面的调用忽略。 - 如果调用then抛出异常e
如果 resolvePromise 或 rejectPromise 已经被调用,那么忽略 否则以 e 为据因拒绝 promise - 如果 then 不是一个function. fulfill promise with x.
规范了解完了下面我们开始手写promise!!!!
这里我们用es6的class来写我们的promise
1.定义一个class来实现Promise
class MPromise{
}
好了,我们的promise类写好了。。。。接下来我们来按照规范来实现一个符合promise A+ 的promise吧。
- 定义三种状态类型
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MPromise{
_status = PENDING;
}
- 初始化状态,value值,reason值
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MPromise{
_status = PENDING;
constructor(){
this.status = PENDING;
this.value = null;
this.reason = null;
}
}
- resolve 和 reject 方法 根据刚才的规范,我们知道两个方法要改变promise的状态,要注意,当promise是pending状态才能更改,当promise更改为最终态后就不可改变了。
resolve(value){
//fulfilled与rejected为最终态不可改变,所以只能当status为pending时才可改变status值
if(this.status === PENDING){
this.status = FULFILLED
this.value = value
}
};
reject(reason){
//fulfilled与rejected为最终态不可改变,所以只能当status为pending时才可改变status值
if(this.status === PENDING){
this.status = REJECTED
this.reason = reason
}
};
- 在promise中我们还需要一个入参,也就是promise(入参),要注意,入参是一个函数,函数接收resolve和reject两个参数。在初始化promise的时候,就要执行,并且如果报错就要reject抛出
class MPromise {
_status = PENDING;
constructor(fn/**入参 */) {
this.status = PENDING;
this.value = null;
this.reason = null;
// 入参初始化时应该执行执行
try {
fn(this.resolve.bind(this), this.reject.bind(this));
} catch (e) {
this.reject(e);
}
}
...(后面的代码目前为resolve方法与reject方法)
}
- then方法(这个关键,有错误请大佬指出)
- 1.then接收两个参数,onFulfilled和onRejected
then(onFulfilled,onRejected){}
- 2.如果onFulfilled或onRejected不是function,就忽略(指原样返回value或reason不做处理)
isFunction(param){
return typeof param === 'function';
}
then(onFulfilled, onRejected){
//写两个三元表达式给他判断一下是不是function
const fulFilledFn = this.isFunction(onFulfilled)?onFulfilled:(value)=>{
return value
}
const rejectedFn = this.isFunction(onRejected)?onRejected:(reason)=>{
//reject直接抛出 reason
throw reason
};
}
- 3.根据当前promise的状态, 调用不同的函数。注意then函数被调用的瞬间就会执行。 那这时候如果status还没变成fulfilled或者rejected。那么我们首先要拿到所有的回调, 然后才能在某个时机去执行他。
class MPromise {
FULFILLED_CALLBACK_LIST = []
REJECTED_CALLBACK_LIST = []
_status = PENDING;
...(省略前面的代码)
then(onFulfilled, onRejected) {
const fulFilledFn = this.isFunction(onFulfilled) ? onFulfilled : (value) => {
return value;
}
const rejectedFn = this.isFunction(onRejected) ? onRejected : (reason) => {
throw reason;
};
switch (this.status) {
case FULFILLED: {
fulFilledFn(this.value);
break;
}
case REJECTED: {
rejectedFn(this.reason);
break;
}
case PENDING: {
this.FULFILLED_CALLBACK_LIST.push(realOnFulfilled);
this.REJECTED_CALLBACK_LIST.push(realOnRejected);
break;
}
}
}
}
- 4.因为onFulfilled特性onRejected特性,在status变成Fulfilled或Rejected时,需要执行所有的回调,这里我们用es6的getter和setter。
get status() {
return this._status;
}
set status(newStatus) {
switch (newStatus) {
case FULFILLED: {
this.FULFILLED_CALLBACK_LIST.forEach(callback => {
callback(this.value);
});
break;
}
case REJECTED: {
this.REJECTED_CALLBACK_LIST.forEach(callback => {
callback(this.reason);
});
break;
}
}
}
-
5.then的返回值 5.1 因为then的返回值是个promis。所有我们需要返回一个prmoise。 5.2 如果onFulfilled 或者 onRejected 抛出一个异常e,则promise2必须拒绝执行,并返回拒因e。我们这时就得手动去catch代码,遇到错误的就直接reject抛出
then(onFulfilled, onRejected) { const fulFilledFn = this.isFunction(onFulfilled) ? onFulfilled : (value) => { return value; } const rejectedFn = this.isFunction(onRejected) ? onRejected : (reason) => { throw reason; }; //这里我们包装两个tryCatch函数分别为 //fulFilledFn(onFulfilled的包装函数)的tryCatch函数 //rejectedFn(onRejected的包装函数)的tryCatch函数 const fulFilledFnWithCatch = (resolve, reject) => { try { fulFilledFn(this.value); } catch (e) { reject(e) } }; const rejectedFnWithCatch = (resolve, reject) => { try { rejectedFn(this.reason); } catch (e) { reject(e); } } switch (this.status) { case FULFILLED: { //返回一个promise return new MPromise(fulFilledFnWithCatch); } case REJECTED: { //返回一个promise return new MPromise(rejectedFnWithCatch); } case PENDING: { //返回一个promise return new MPromise((resolve, reject) => { this.FULFILLED_CALLBACK_LIST.push(() => fulFilledFnWithCatch(resolve, reject)); this.REJECTED_CALLBACK_LIST.push(() => rejectedFnWithCatch(resolve, reject)); }); } } }5.2 如果 onFulfilled 不是函数且 promise1 成功执行, promise2 必须成功执行并返回相同的值
const fulFilledFnWithCatch = (resolve, reject) => { try { fulFilledFn(this.value); resolve(this.value); } catch (e) { reject(e) } };5.3 如果 onRejected 不是函数且 promise1 拒绝执行, promise2 必须拒绝执行并返回相同的据因。 需要注意的是,如果promise1的onRejected执行成功了,promise2应该被resolve。(这里很绕,可以运行一个promise看看。第一个then成功执行后promise会执行onFulfilled方法)
const rejectedFnWithCatch = (resolve, reject) => {
try {
rejectedFn(this.reason);
if (this.isFunction(onRejected)) {
resolve();
}
} catch (e) {
reject(e);
}
}
5.4 如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行resolvePromise方法
resolvePromise(newPromise, x, resolve, reject)
因为resolvePromise需要一个newPromise参数,所以我们需要把then函数再改造一下。
then(onFulfilled, onRejected) {
const fulFilledFn = this.isFunction(onFulfilled) ? onFulfilled : (value) => {
return value;
}
const rejectedFn = this.isFunction(onRejected) ? onRejected : (reason) => {
throw reason;
};
const fulFilledFnWithCatch = (resolve, reject, newPromise) => {
try {
if (!this.isFunction(onFulfilled)) {
resolve(this.value);
} else {
const x = fulFilledFn(this.value);
this.resolvePromise(newPromise, x, resolve, reject);
}
} catch (e) {
reject(e)
}
};
const rejectedFnWithCatch = (resolve, reject, newPromise) => {
try {
if (!this.isFunction(onRejected)) {
reject(this.reason);
} else {
const x = rejectedFn(this.reason);
this.resolvePromise(newPromise, x, resolve, reject);
}
} catch (e) {
reject(e);
}
}
switch (this.status) {
case FULFILLED: {
const newPromise = new MPromise((resolve, reject) => fulFilledFnWithCatch(resolve, reject, newPromise));
return newPromise;
}
case REJECTED: {
const newPromise = new MPromise((resolve, reject) => rejectedFnWithCatch(resolve, reject, newPromise));
return newPromise;
}
case PENDING: {
const newPromise = new MPromise((resolve, reject) => {
this.FULFILLED_CALLBACK_LIST.push(() => fulFilledFnWithCatch(resolve, reject, newPromise));
this.REJECTED_CALLBACK_LIST.push(() => rejectedFnWithCatch(resolve, reject, newPromise));
});
return newPromise;
}
}
}
- resolvePromise
resolvePromise(newPromise, x, resolve, reject) {
// 如果 newPromise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 newPromise
// 这是为了防止死循环
if (newPromise === x) {
return reject(new TypeError('The promise and the return value are the same'));
}
if (x instanceof MPromise) {
// 如果 x 为 Promise ,则使 newPromise 接受 x 的状态
// 也就是继续执行x,如果执行的时候拿到一个y,还要继续解析y
// 这个if跟下面判断then然后拿到执行其实重复了,可有可无
x.then((y) => {
resolvePromise(newPromise, y, resolve, reject);
}, reject);
} else if (typeof x === 'object' || this.isFunction(x)) {
// 如果 x 为对象或者函数
// 这个坑是跑测试的时候发现的,如果x是null,应该直接resolve
if (x === null) {
return resolve(x);
}
let then = null;
try {
// 把 x.then 赋值给 then
then = x.then;
} catch (error) {
// 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
return reject(error);
}
// 如果 then 是函数
if (this.isFunction(then)) {
let called = false;
// 将 x 作为函数的作用域 this 调用之
// 传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise
// 名字重名了,我直接用匿名函数了
try {
then.call(
x,
// 如果 resolvePromise 以值 y 为参数被调用,则运行 resolvePromise
(y) => {
// 如果 resolvePromise 和 rejectPromise 均被调用,
// 或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
// 实现这条需要前面加一个变量called
if (called) return;
called = true;
resolvePromise(promise, y, resolve, reject);
},
// 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
(r) => {
if (called) return;
called = true;
reject(r);
});
} catch (error) {
// 如果调用 then 方法抛出了异常 e:
// 如果 resolvePromise 或 rejectPromise 已经被调用,则忽略之
if (called) return;
// 否则以 e 为据因拒绝 promise
reject(error);
}
} else {
// 如果 then 不是函数,以 x 为参数执行 promise
resolve(x);
}
} else {
// 如果 x 不为对象或者函数,以 x 为参数执行 promise
resolve(x);
}
}