关于promise:遵循PromisesA规范深入分析Promise实现细节-通过872测试样例

前言
本周写文的外围为 Promise ,Promise 大家应该都特地相熟了,Promise 是异步编程的一种解决方案,宽泛用在日常编程中。本周小包将围绕 Promise 源码手写进行写文,源码手写初步打算应用三篇文章实现—— 手写 Promise 之根底篇,手写 Promise 之 resolvePromise 篇,手写 Promise 之静态方法篇。

Promises/A+ 标准是 Promise 的实现准则,因而 Promise 手写系列将遵循 Promises/A+ 标准的思路,以案例和发问形式层层深刻,一步一步实现 Promise 封装。

学习本文,你能播种:

🌟 了解 Promise A+标准
🌟 了解什么是 Promise 的值穿透、Promise 链式调用机制、Promise 注册多个 then 办法等。
🌟 把握 Promise 源码编写全过程
🌟 把握公布订阅模式在 Promise 源码编写中的应用
根底铺垫
Promise 必然处于下列三种状态之一:

Pending 期待态: 初始状态,不是胜利或失败状态。
Fulfilled 实现态: 意味着操作胜利实现。
Rejected 失败态: 意味着操作成功失败。
当 promise 处于 Pending 状态时,能够转变为 Fulfilled 或者 Rejected
当 promise 处于 Fulfilled 或 Rejected 时,状态不能再产生扭转

那什么会触发 promise 中状态的扭转呐?咱们来看几个栗子:

// p1 什么都不执行且传入空函数
const p1 = new Promise(() => {});
console.log("p1: ", p1);

// p2 执行 resolve
const p2 = new Promise((resolve, reject) => {
  resolve("success");
});
console.log("p2: ", p2);

// p3 执行 reject
const p3 = new Promise((resolve, reject) => {
  reject("fail");
});
console.log("p3: ", p3);

// p4 抛出谬误
const p4 = new Promise((resolve, reject) => {
  throw Error("error");
});
console.log("p4: ", p4);

// p5 先执行 resolve 后执行 reject
const p5 = new Promise((resolve, reject) => {
  resolve("success");
  reject("fail");
});
console.log("p5: ", p5);

// p6 什么都不执行且不传参
const p6 = new Promise();
console.log("p6: ", p6);
复制代码

咱们来看一下输入后果:

从输入后果咱们能够发现:

创立 promise 对象时,需传入一个函数(否则会报错,详见 p6),并且该函数会立刻执行
promise 的初始状态为 Pending(见 p1)
执行 resolve() 和 reject() 能够将 promise 的状态批改为 Fulfilled 和 Rejected (见 p2,p3)
若 promise 中抛出异样,相当于执行 reject (见 p4)
promise 状态转变只能由 Pending 开始(见 p5)
依据咱们对输入后果的剖析,咱们来编写 promise 的第一版代码。

实现根底 promise —— 第一版
promise 构造函数实现
首先定义 promise 的三种状态

const PENDING = "PENDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";
复制代码

定义 Promise 构造函数,增加必备属性
Promises/A+ 标准中指出:

value 是任意的 JavaScript 非法值(包含 undefined)
reason 是用来示意 promise 为什么被回绝的起因
咱们应用 ES6 class 定义 Promise 类,value/reason 别离赋值为 undefined ,状态 status 初始为 PENDING

class Promise {
  constructor() {
    this.value = undefined;
    this.reason = undefined;
    this.status = PENDING;
  }
}
复制代码

定义 promise 时须要传入函数 executor
executor 有两个参数,别离为 resolve,reject,且两个参数都是函数
executor 会立刻执行

class Promise {
  constructor(executor) {
    this.value = undefined;
    this.reason = undefined;
    this.status = PENDING;
    // 定义resolve 和 reject 函数
    const resolve = () => {};
    const reject = () => {};
    // 结构器立刻执行
    executor(resolve, reject);
  }
}
复制代码

实现 resolve 和 reject 的性能
当 promise 状态为 Pedding 时: resolve 函数能够将 promise 由 Pending 转变为 Fulfilled,并且更新 promise 的 value 值。reject 函数能够将 promise 由 Pending 转变为 Rejected,并且更新 promise 的 reason 值

留神: promise 状态只能由 Pending -> Fulfilled 和 Pending -> Rejected

因而在定义 resolve 和 reject 函数时,外部须要先判断 promise 的状态,如果状态为 pending ,才能够更新 value 值和 promise 状态。

class Promise {
  constructor(executor) {
    this.value = undefined;
    this.reason = undefined;
    this.status = PENDING;

    const resolve = (value) => {
      // 判断以后的状态是否为Pending
      // promise状态转变只能从 Pending 开始
      if (this.status === PENDING) {
        // 更新 value 值和 promise 状态
        this.value = value;
        this.status = FULFILLED;
      }
    };
    const reject = (reason) => {
      if (this.status === PENDING) {
        this.reason = reason;
        this.status = REJECTED;
      }
    };

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

源码写到这里,小包就产生纳闷了本文第一个疑难,来,上问题。

发问一: resolve/reject 函数为什么应用箭头函数定义?
问题答案小包这里先不讲,大家先思考思考,到文末小包一块答复。

Promise A+ 标准规定,Promise 执行抛出异样时,执行失败函数。因而咱们须要捕捉 executor 的执行,如果存在异样,执行 reject 函数。

class Promise {
    // ...多余代码先暂省略
    // 捕捉 executor 异样
    try {
      executor(resolve, reject);
    } catch (e) {
      // 当产生异样时,调用 reject 函数
      reject(e);
    }
  }
}
复制代码

咱们实现完了 Promise 的主体局部,上面就来实现 Promise 的另一重要外围 then 办法。

实现 then 办法的基本功能
then 办法的注意事项比拟多,咱们一起来浏览标准顺带举例说明一下。

promise.then 承受两个参数:

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

定义 then 函数,接管两个参数

class Promise {
  then (onFulfilled, onRejected) {}
}
复制代码

onFulfilled 和 onRejected 是可选参数,两者如果不是函数,则会疏忽掉(真的是简略的疏忽掉吗?请看下文值穿透)
如果 onFulfilled 是一个函数,当 promise 状态为 Fulfilled 时,调用 onFulfilled 函数,onRejected 相似,当 promise 状态为 Rejeted 时调用。
咱们持续来看几个栗子:

// 执行 resolve
const p1 = new Promise((resolve, reject) => {
  resolve(1);
});
p1.then(
  (v) => {
    console.log("onFulfilled: ", v);
  },
  (r) => {
    console.log("onRejected: ", r);
  }
);

// 执行 reject
const p2 = new Promise((resolve, reject) => {
  reject(2);
});
p2.then(
  (v) => {
    console.log("onFulfilled: ", v);
  },
  (r) => {
    console.log("onRejected: ", r);
  }
);

// 抛出异样
const p3 = new Promise((resolve, reject) => {
  throw new Error("promise执行呈现谬误");
});
p3.then(
  (v) => {
    console.log("onFulfilled: ", v);
  },
  (r) => {
    console.log("onRejected: ", r);
  }
);
复制代码

咱们来看一下输入后果:

通过输入后果,咱们能够发现 then 的调用逻辑

执行 resolve 后,promise 状态扭转为 Fulfilled,onFulfilled 函数调用,参数值为 value。
执行 reject 或 抛出谬误,promise 状态扭转为 Rejected ,onRejected 函数调用,参数值为 reason。
接下来,咱们来剖析一下 then 的实现思路。

then 函数中判断 promise 以后的状态,如果为 Fulfilled 状态,执行 onFulfilled 函数;Rejected 状态,执行 onRejected 函数。实现思路很简略,那上面咱们就来实现一下。

class Promise {
  then(onFulfilled, onRejected) {
    // 当状态为 Fulfilled 时,调用 onFulfilled函数
    if (this.status === FULFILLED) {
      onFulfilled(this.value);
    }
    // 当状态为 Rejected 时,调用 onRejected 函数
    if (this.status === REJECTED) {
      onRejected(this.reason);
    }
  }
}
复制代码

发问二: then 办法执行时 promise 状态会呈现 Pending 状态吗
promise 注册多个 then 办法
咱们持续往下读标准:

如果一个 promise 调用屡次 then: 当 promise 状态为 Fulfilled 时,所有的 onFulfilled 函数依照注册顺序调用。当 promise 状态为 Rejected 时,所有的 onRejected 函数依照注册顺序调用。

这个标准讲的是什么意思那?小包来举个栗子:

const p = new Promise((resolve, reject) => {
  resolve("success");
});

p.then((v) => {
  console.log(v);
});
p.then((v) => {
  console.log(`${v}--111`);
});
复制代码

输入后果:

success;
success---111;
复制代码

通过下面的案例,该标准艰深来讲: 同一个 promise 能够注册多个 then 办法,当 promise 实现或者失败后,对应的 then 办法依照注册程序顺次执行。

该标准咱们的代码曾经能够兼容。学到这里,咱们整合一下 Promise 第一版代码,并对目前所写代码进行测试。

// promise 三种状态
// 状态只能由 PENDING -> FULFILLED/REJECTED
const PENDING = "PENDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";

class Promise {
  constructor(executor) {
    this.value = undefined;
    this.reason = undefined;
    // 初始状态为 Pending
    this.status = PENDING;
    // this指向问题
    const resolve = (value) => {
      // 判断以后的状态是否为Pending
      // promise状态转变只能从 Pending 开始
      if (this.status === PENDING) {
        // 更新 value 值和 promise 状态
        this.value = value;
        this.status = FULFILLED;
      }
    };
    const reject = (reason) => {
      if (this.status === PENDING) {
        this.reason = reason;
        this.status = REJECTED;
      }
    };
    try {
      // 捕捉 executor 异样
      executor(resolve, reject);
    } catch (e) {
      // 当产生异样时,调用 reject 函数
      reject(e);
    }
  }
  then(onFulfilled, onRejected) {
    // 当状态为 Fulfilled 时,调用 onFulfilled函数
    if (this.status === FULFILLED) {
      onFulfilled(this.value);
    }
    // 当状态为 Rejected 时,调用 onRejected 函数
    if (this.status === REJECTED) {
      onRejected(this.reason);
    }
  }
}
复制代码

先来测试根底局部的案例,输入后果如下:

再来测试同一 Promise 注册多个 then 办法,输入后果为

success;
success---111;
复制代码

第一版代码是能够满足以后标准的,~~~,放松一下,咱们来持续实现。

解决异步性能——第二版
文章刚开始咱们就讲过,promise 是异步编程的一种解决方案,那咱们来测试一下第一版 Promise 是否能够实现异步。

const p = new Promise((resolve, reject) => {
  // 应用 setTimeout 模仿一下异步
  setTimeout(() => {
    resolve("success");
  });
});

p.then((v) => {
  console.log(v);
});
p.then((v) => {
  console.log(`${v}--111`);
});
复制代码

没有任何输入,可见第一版代码到目前是无奈实现异步编程的,咱们来剖析一下起因。

如果 Promise 外部存在异步调用,当执行到 then 函数时,此时因为 resolve/reject 处于异步回调之中,被阻塞未能调用,因而 promise 的状态仍为 Pending,第一版 then 回调中的 onFulfilled 和 onRejected 无奈执行。

公布订阅模式
为了更好的实现原生 promise 的编写,在这里咱们插补一点常识。

异步编程中有一个常常应用的思维,叫做公布订阅模式。公布订阅模式是指基于一个事件(主题)通道,心愿接管告诉的对象 Subscriber 通过自定义事件订阅主题,被激活事件的对象 Publisher 通过公布主题事件的形式告诉各个订阅该主题的 Subscriber 对象。

公布订阅模式中有三个角色,发布者 Publisher ,事件通道 Event Channel ,订阅者 Subscriber 。

光凭借定义有点难以了解,小包举一个栗子: 以目前的热播剧人世间为例,人世间切实太火了,工作时候也安不下心,每天就急不可待的等人世间更新,想在人世间更新的第一刻就开始看剧,那你应该怎么做呐?总不能时时刻刻刷新页面,监测人世间是否更新。平台是人性化的,其提供了音讯订阅性能,如果你抉择订阅,平台更新人世间后,会第一工夫发消息告诉你,订阅后,你就能够欢快的追剧了。

那咱们要怎么设计 Promise 的异步性能呐? 咱们把 Promise 的性能依照公布订阅模式合成一下:

then 回调 onFulfilled/onRejected 函数
resolve/reject 函数
resolve/reject 函数执行后,promise 状态扭转,then 回调函数执行
只有当 resolve/reject 函数执行后,对应 onFulfilled/onRejected 才能够执行执行,但因为存在异步调用,resolve/reject 执行晚于 then 函数。因而 onFulfilled/onRejected 就能够了解为订阅者,订阅 resolve/reject 函数执行;resolve/reject 是发布者;Promise 提供事件通道作用,存储订阅的 onFulfilled/onRejected 。因为同一个 promise 对象能够注册多个 then 回调,因而 Event Channel 存储回调应为数组格局

因而咱们须要批改 resolve/reject 函数的实现,当两者被调用时,同时告诉对应订阅者执行。

异步实现
在 Promise 中定义两个数组 onFulfilledCallbacks 和 onRejectedCallbacks ,别离用来存储 then 回调 onFulfilled 和 onRejected 函数

class Promise {
  // 存储订阅的onFulfilled函数和onRejected函数
  this.onFulfilledCallbacks = [];
  this.onRejectedCallbacks = [];
}
复制代码

then 办法执行时,若 Promise 处于 Pending 状态,将 onFulfilled 和 onRejected 函数别离订阅至 onFulfilledCallbacks 和 onRejectedCallbacks——期待 resolve/reject 执行(事件公布)

then(onFulfilled, onRejected) {
    if (this.status === PENDING) {
        // 当promise处于pending状态时,回调函数订阅
        this.onFulfilledCallbacks.push(onFulfilled);
        this.onRejectedCallbacks.push(onRejected);
    }
}
复制代码

调用 resolve/reject 时,公布事件,别离执行对应 onFulfilledCallbacks 和 onRejectedCallbacks 数组中的函数

// 执行公布
const resolve = (value) => {
  if (this.status === PENDING) {
    this.value = value;
    this.status = FULFILLED;
    // 顺次执行onFulfilled函数
    this.onFulfilledCallbacks.forEach((cb) => cb(this.value));
  }
};
const reject = (reason) => {
  if (this.status === PENDING) {
    this.reason = reason;
    this.status = REJECTED;
    // 顺次执行onRejected函数
    this.onRejectedCallbacks.forEach((cb) => cb(this.reason));
  }
};
复制代码

咱们将上述代码进行汇总,造成第二版代码,并进行案例测试。

// 异步调用
const PENDING = "PENDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";

class Promise {
  constructor(executor) {
    this.value = undefined;
    this.reason = undefined;
    this.status = PENDING;
    // 存储订阅的onFulfilled函数和onRejected函数
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];
    const resolve = (value) => {
      if (this.status === PENDING) {
        this.value = value;
        this.status = FULFILLED;
        // 当 resolve 函数调用时,告诉订阅者 onFulfilled 执行
        this.onFulfilledCallbacks.forEach((cb) => cb(this.value));
      }
    };
    const reject = (reason) => {
      if (this.status === PENDING) {
        this.reason = reason;
        this.status = REJECTED;
        // 当 reject 函数调用时,告诉订阅者 onRejected 执行
        this.onRejectedCallbacks.forEach((cb) => cb(this.reason));
      }
    };
    try {
      executor(resolve, reject);
    } catch (e) {
      console.log(e);
      reject(e);
    }
  }
  then(onFulfilled, onRejected) {
    if (this.status === FULFILLED) {
      onFulfilled(this.value);
    }
    if (this.status === REJECTED) {
      onRejected(this.reason);
    }
    if (this.status === PENDING) {
      // 当promise处于pending状态时,回调函数订阅
      this.onFulfilledCallbacks.push(onFulfilled);
      this.onRejectedCallbacks.push(onRejected);
    }
  }
}
复制代码

应用方才的案例进行测试,输入后果为

success
success--111
复制代码

下面的案例有些简略,咱们再来测试一个简单的案例:

console.log(1);
setTimeout(() => {
  console.log(2);
})
const p1 = new Promise((resolve) => {
  console.log(3);
  setTimeout(() => {
    resolve(4);
  })
})
p1.then(v => console.log(v));
console.log(5);
复制代码

浏览器输入后果:

第二版代码输入后果:

浏览器与第二版输入的后果是雷同的,因而可见目前第二版 Promise 是能够实现异步性能的。

但真的没问题吗?咱们把案例略微批改,去掉 Promise 中的异步调用,看浏览器输入后果是否与第二版雷同。

console.log(1);
setTimeout(() => {
  console.log(2);
})
const p1 = new Promise((resolve) => {
  console.log(3);
  resolve(4);
})
p1.then(v => console.log(v));
console.log(5);
复制代码

浏览器输入后果:

第二版代码输入后果:

咱们能够显著的发现第二版代码与浏览器的2 4 输入是相同的?可见浏览器中先执行 then 办法,后执行 setTimeout?

发问三: 为什么浏览器会先执行 then 办法回调,后执行 setTimeout 那?
链式调用——第三版
异步性能实现结束,咱们持续去实现 then 办法的链式调用。首先咱们持续去读标准:

then 办法必须返回一个 promise

promise2 = promise1.then(onFulfilled, onRejected)
复制代码

promise2 是 then 函数的返回值,同样是一个 Promise 对象。

then(onFulfilled, onRejected) {
  // ... 多余代码省略
  cosnt promise2 = new Promise((resolve, reject) => {})
  return promise2;
}
复制代码

如果 onFulfilled 或 onRejected 返回值为 x ,则运行 Promise Resolution Procedure [[Resolve]](promise2, x)(这里暂且将他了解为执行 promise2 的 resolve(x)函数)
咱们来举栗子了解一下此条标准:

// 案例1 resolve
console.log(new Promise((resolve) => {
    resolve(1)
}).then((x) => x))
// 案例2 reject
console.log(new Promise((resolve, reject) => {
    reject(1)
}).then(undefined,(r) => r))
复制代码


咦,怎么两者返回后果一样,明明 promise 中别离执行 resolve 和 reject 函数。

咱们再来详读一遍标准:

如果 onFulfilled 或 onRejected 返回值为 x ——下面两个函数都 (v) => v,传入参数值都是 1,因而返回值 x = 1;
则执行 promise2 的 resolve(x)函数,而后 then 返回 promise2 对象——因而下面两个函数都是调用 promise2 的 resolve 函数,所以两者返回值都是处于 fulfilled 状态的 promise 对象,并且值都为 1。
因为咱们须要将 onFulfilled/onRejected 函数返回值作为 promise2 resolve 的参数值,因而咱们须要将 then 函数整体挪动至 promise2 外部。

then (onFulfilled, onRejected) {
  let promise2 = new Promise((resolve, reject) => {
      if (this.status === FULFILLED) {
        // 返回值作为 resolve 的参数值
        let x = onFulfilled(this.value);
        resolve(x);
      }
      if (this.status === REJECTED) {
        let x = onRejected(this.reason);
        resolve(x);
      }
    });
    return promise2;
}
复制代码

你认为这样就能实现这条标准了吗?NONONO!!!

难点: 同步代码上述思路确实能够实现,但构想这样一个场景,若 Promise 中存在异步代码,异步逻辑设计是 then 执行时,若 Promise 处于 Pending 状态,先将 onFulfilled/onRejected 函数订阅到 onFulfilledCallbacks/onRejectedCallbacks 中,意味着在 then 中此时两函数不会执行,那么此咱们应该如何获取两者的返回值那?

因而咱们不能单纯的应用 this.onFulfilledCallbacks.push(onFulfilled) 将回调函数压入事件通道的存储数组中,咱们对回调函数做一层封装,将 promise2 的 resolve 函数和 onFulfilled 封装在一起,这样当 onFulfilled 执行时,能够获取其返回值 x ,返回 fulfilled 状态的 promise2,具体能够看上面代码:

// 应用匿名箭头函数,保障外部 this 指向
() => {
  // 回调函数执行,获取其返回值
  let x = onFulfilled(this.value);
  // 执行 promise2 的 resolve 办法
  resolve(x);
}
复制代码

因而 Pending 状态的代码如下:

if (this.status === PENDING) {
  // 应用匿名函数,将 resovle 与 onFulfilled 捆绑在一起
  this.onFulfilledCallbacks.push(() => {
    let x = onFulfilled(this.value);
    resolve(x);
  });
  this.onRejectedCallbacks.push(() => {
    let x = onRejected(this.reason);
    resolve(x);
  });
}
复制代码

如果 onFulfilled 或 onRejected 执行过程中抛出异样 e ,则调用 promise2 的 reject(e),返回 promise2
咱们还是举栗子测试一下:

console.log(new Promise((resolve) => {
    resolve(1)
}).then(()=> {
    throw new Error('resolve err')
}))
console.log(new Promise((resolve, reject) => {
    reject(1)
}).then(undefined,()=> {
    throw new Error('reject err')
}))
复制代码


通过输入后果,咱们能够看出当 onFulfilled/onRejected 函数报错时,promise2 会执行其 reject 函数。因而咱们须要给目前的代码增加一层异样捕捉,将代码批改成如下状况:

then(onFulfilled, onRejected) {
  let p1 = new Promise((resolve, reject) => {
    if (this.status === FULFILLED) {
      // 增加异样捕捉
      try {
        // 返回值作为 resolve 的参数值
        let x = onFulfilled(this.value);
        resolve(x);
      } catch (e) {
        reject(e);
      }
    }
    //... 其余部分相似
  return promise2;
}
复制代码

如果 onFulfilled 不是函数,且 promise 状态为 Fulfilled ,那么 promise2 应该承受同样的值,同时状态为 Fulfilled
这个标准是啥意思呐?咱们来举一个栗子:

// 输入后果 1
const p1 = new Promise((resolve) => {
    resolve(1)
})
p1.then(x => x).then().then().then().then().then(x=> console.log(x))
复制代码
上述程序最终输入后果为 1 ,首次 resolve 传递的 value 值为 1,可见当 onFulfilled 不是函数时, promise 值会沿 then 产生传递,直到 onFulfilled 为函数。

这也就是 Promise 的值传递,当 then 的 onFulfilled 为非函数时,值会始终传递上来,直至遇到函数 onFulfilled

如果 onRejected 不是函数,且 promise 状态为Rejected,那么 promise2 应该承受同样的起因,同时状态为 Rejected
// 输入后果 Error: error at <anonymous>:4:33
const p1 = new Promise((resolve) => {
    reject(1)
})
p1.then(undefined, () => {throw Error('error')}).then().then().then().then().then(x=> console.log(x), (r)=> console.log(r))
复制代码

与 onFulfilled 相似,Promise 同样提供了对onRejected 函数的兼容,会产生谬误传递。

通过第 4 条与第 5 条的案例,咱们能够发现,当 onFulfilled/onRejected 为非函数类型,Promise 会别离产生值传递和异样传递。

咱们如何能力间断传递值或者异样那?(见上面代码)

值传递: 值传递非常简单,咱们只须要定义一个函数,参数值为 x ,返回值为 x
异样: 定义函数参数值为异样,之后一直抛出此异样。

x => x;
e => throw e;
复制代码
then(onFulfilled, onRejected) {
  // 判断参数是否为函数,如果不是函数,应用默认函数代替
  onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (v) => v;
  onRejected =
    typeof onRejected === "function"
      ? onRejected
      : (e) => {
          throw e;
        };
  let promise2 = new Promise((resolve, reject) => {
  });
  return promise2;
}
复制代码

写到这里,链式调用的局部就临时实现了,咱们整合一下第三版 Promise 代码。

const PENDING = "PENDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";

class Promise {
  constructor(executor) {
    this.value = undefined;
    this.reason = undefined;
    this.status = PENDING;
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];
    const resolve = (value) => {
      if (this.status === PENDING) {
        this.value = value;
        this.status = FULFILLED;
        this.onFulfilledCallbacks.forEach((cb) => cb(this.value));
      }
    };
    const reject = (reason) => {
      if (this.status === PENDING) {
        this.reason = reason;
        this.status = REJECTED;
        this.onRejectedCallbacks.forEach((cb) => cb(this.reason));
      }
    };
    try {
      executor(resolve, reject);
    } catch (e) {
      reject(e);
    }
  }
  then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (v) => v;
    onRejected =
      typeof onRejected === "function"
        ? onRejected
        : (e) => {
            throw e;
          };
    let promise2 = new Promise((resolve, reject) => {
      if (this.status === FULFILLED) {
        // 增加异样捕捉
        try {
          // 返回值作为 resolve 的参数值
          let x = onFulfilled(this.value);
          resolve(x);
        } catch (e) {
          reject(e);
        }
      }
      if (this.status === REJECTED) {
        try {
          let x = onRejected(this.reason);
          resolve(x);
        } catch (e) {
          reject(e);
        }
      }
      if (this.status === PENDING) {
        // 应用匿名函数,将 resovle 与 onFulfilled 捆绑在一起
        this.onFulfilledCallbacks.push(() => {
          try {
            let x = onFulfilled(this.value);
            resolve(x);
          } catch (e) {
            reject(e);
          }
        });
        this.onRejectedCallbacks.push(() => {
          try {
            let x = onRejected(this.reason);
            resolve(x);
          } catch (e) {
            reject(e);
          }
        });
      }
    });
    return promise2;
  }
}
复制代码

咱们测试一下是否能够实现链式调用:

// 输入后果为 4,能够阐明resolve状态的链式调用是可行的,并且实现了值传递
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(1);
  });
});
p1.then((v) => v + 1)
  .then((v) => v * 2)
  .then()
  .then((v) => console.log(v));

// 输入 Error1,阐明链式调用依然是胜利的。
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(1);
  });
});
p2.then(
  () => {},
  (r) => new Error(r)
).then(
  (v) => console.log("v", v),
  (r) => console.log("r", r)
);
复制代码

写到这里,第三版代码就实现胜利了,前面还有最外围的 resolvePromise 局部,该局部比较复杂,因而小包决定专门开一篇文章具体讲述。

问题答复
resolve/reject 函数为什么应用箭头函数定义?
一句话解释: this 指向问题。

咱们将其批改为一般函数模式:

class Promise {
  constructor(executor) {
    this.value = undefined;
    this.reason = undefined;
    const resovle = function (value) {
      console.log(this);
      this.value = value;
    }
    const reject = (reason) => {
      console.log(this);
      this.reason = reason;
    }
    executor(resovle, reject)
  }
}

复制代码

之后咱们别离执行以下代码:

var value = 1;
new Promise((resolve, reject) => {
resolve(100)
})
复制代码

从后果咱们能够发现: this 的输入后果为 undefined 。因为 resolve 是一个一般函数,在 Promise 中调用为默认调用,this 非严格模式指向 window ,严格模式指向 undefined。 ES6 class 默认为严格模式,因而指向 undefined。所以应用一般函数,咱们获取不到 Promise 中的 value 属性。

// 输入后果 Promise {value: undefined, reason: 200}
var reason = 2;
new Promise((resolve, reject) => {
  reject(200)
})
复制代码

reject 应用箭头函数,箭头函数本身没有 this ,因而会沿作用域链应用外层作用域的 this。所以咱们能够获取到 reason 属性。

then 办法执行时 promise 状态会呈现 pending 状态吗
会呈现,文章中曾经提到了,当 Promise 中存在异步代码时,例如

new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(1)
  })
})
复制代码

为什么浏览器会先执行 then 办法回调,后执行 setTimeout 那?
这是 JavaScript 的事件机制(Event Loop)导致的,then 回调为微工作,setTimeout 为宏工作,当同步代码执行结束后,主程序会先去微工作队列寻找工作,微工作队列全副执行结束,才会执行宏工作队列。

最初
如果你感觉此文对你有一丁点帮忙,点个赞。或者能够退出我的开发交换群:1025263163互相学习,咱们会有业余的技术答疑解惑

如果你感觉这篇文章对你有点用的话,麻烦请给咱们的开源我的项目点点star:http://github.crmeb.net/u/defu不胜感激 !

PHP学习手册:https://doc.crmeb.com
技术交换论坛:https://q.crmeb.com

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理