之前在JS异步函数小结里面初步介绍了JS里面的一些异步操作与 Promise 的使用方法,我们要知其然也要知其所以然,所以让我们看一下如何动手实现自己的 MyPromise。
协议是实现的基础,Promise/A+协议的中文参考网站如下:
https://www.ituring.com.cn/article/66566
英文网站如下:
-
首先Promise构造参数只能接受函数,否则报错。
-
Promise内部变量包括:
- 值 value => 指任何 JavaScript 的合法值(包括 undefined , thenable 和 promise)。
- 拒因 reason => 值一个 promise 拒绝的原因。
- 状态 state => promise 执行所处的状态
- Promise规范规定了Promise的状态一共有三种:Pending、Fulfilled 及 Rejected。状态之间的转换只能是 Pending => Fulfilled 或者 Pending => Rejected,状态的转换不可逆转。
初步实现:
// MyPromise.js
class MyPromise{
constructor(executor){
// Promise的构造函数参数必须是函数,否则报错
if(typeof executor !== "function"){
throw new TypeError(`Promise resolver ${executor} is not a function`);
}
this.initValue();
}
// 内部参数初始化
initValue(){
this.value = null; // 值
this.reason = null; //据因
this.state = MyPromise.PENDING; // 初始状态
}
}
MyPromise.PENDING = "Pending";
MyPromise.FULFILLED = "Fulfilled";
MyPromise.REJECTED = "Rejected";
- 同时 Promise 采用两个回调函数作为值 value 传递的方法,分别是 resolve 函数 及 reject 函数,分别对应 Fulfilled 状态及 Rejected 状态的函数回调,回调函数分别为 OnFulfilled 及 OnRejected。
class MyPromise{
// ...
then(OnFulfilled, OnRejected){
// promise 执行成功后回调
if(this.state === MyPromise.FULFILLED){
OnFulfilled(this.value);
}
// promise 执行失败后回调
if(this.state === MyPromise.REJECTED){
OnRejected(this.value);
}
}
}
测试
let promise = new MyPromise('1'); // 报错 TypeError: Promise resolver 1 is not a function
let promise2 = new MyPromise((resolve, reject)=>{
resolve(1);
});
promise2.then(data=>console.log(data)); // 打印 1可以执行
问题
如果promise函数内存在异步,那么 state 状态仍为 Pending 但是 then 函数为同步执行,导致 value 没有同步传递,没有输出。
let promise3 = new MyPromise((resolve, reject)=>{
setTimeout(()=>{
resolve(1);
})
});
promise3.then(data=>console.log(data)) // 没有输出
解决
添加 state 为 Pending 状态时的处理,并用两个任务队列 OnFulfilledCallbacks 及 OnRejectedCallbacks 分别存储状态为 OnFulfilled 及 OnRejected 的异步执行回调函数,然后等 resolve 函数执行完毕再调用回调函数。
// MyPromise.js
// resolve 函数执行,state 变为 FULFILLED
resolve(value) {
// ...
this.OnFulfilledCallbacks.forEach(cb => cb(this.value));
}
// reject 函数执行,state 变为 REJECTED
reject(reason) {
// ...
this.OnRejectedCallbacks.forEach(cb => cb(this.reason));
}
then(OnFulfilled, OnRejected) {
// ...
// 异步执行函数状态为 Pending 时的处理
if (this.state === MyPromise.PENDING) {
this.OnFulfilledCallbacks.push(
(value) => setTimeout(() => {
OnFulfilled(value);
}));
this.OnRejectedCallbacks.push(
(reason) => setTimeout(() => {
OnRejected(reason);
}));
}
}
let promise3 = new MyPromise((resolve, reject)=>{
setTimeout(()=>{
resolve(1);
})
});
promise3.then(data=>console.log(data)) // 输出 1
根据 Promise/A+ 的规范定义,Promise的 then 函数返回一个 promise2 以支持链式调用,因此对代码进行修改:
then(OnFulfilled, OnRejected) {
if(typeof OnFulfilled !== "function"){
// 如果 OnFulfilled 不是函数或者为空就支持链式调用 .then().then()
OnFulfilled = function(value){
return value;
}
}
if(typeof OnRejected !== "function"){
OnRejected = function(reason){
throw reason;
}
}
let promise2 = new MyPromise((resolve, reject) => {
// promise 执行成功后回调
if (this.state === MyPromise.FULFILLED) {
setTimeout(() => {
// 上一个 Promise回调函数返回的值交给下一个 Promise调用
try{
const x = OnFulfilled(this.value);
resolve(x);
}catch(e){
reject(e);
}
})
}
// promise 执行失败后回调
if (this.state === MyPromise.REJECTED) {
setTimeout(() => {
try{
const x = OnRejected(this.reason);
resolve(x);
}catch(e){
reject(e);
}
})
}
// 异步执行函数状态为 Pending 时的处理
if (this.state === MyPromise.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(reason);
resolve(x);
}catch(e){
reject(e);
}
}));
}
});
return promise2;
}
测试
let promise4 = new MyPromise((resolve, reject)=>{
setTimeout(()=>{
resolve(2);
})
});
promise4.then().then().then(data=>console.log(data)) // 2 支持链式调用
首先定义一个 resolveMyPromise 的处理方法
MyPromise.resolveMyPromise = function(promise2, x, resolve, reject){
// 循环调用 thenable 方法时只调用一次
let called = false;
// 如果返回的 x 等于 promise2,则报错,避免循环调用
if(x===promise2){
return reject(new new TypeError("cannot return the same promise object from onfulfilled or on rejected callback."))
}
if(x instanceof MyPromise){
}
// 避免null也为object
else if( (x!==null && typeof x === "object") || typeof x ==="function"){
}else{
// 如果不是 MyPromise 或者不是具有 thenable 的类及方法,就直接传入 x 数据
resolve(x);
}
}
根据 Promise/A+ 的规范依次实现 resolveMyPromise 方法
MyPromise.resolveMyPromise = function(promise2, x, resolve, reject){
// 循环调用 thenable 方法时只调用一次
let called = false;
// 如果 promise2 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise
if(x===promise2){
reject(new new TypeError("cannot return the same promise object from onfulfilled or on rejected callback."))
}
// x 为 Promise
if(x instanceof MyPromise){
// 如果 x 为 Promise ,则使 promise 接受 x 的状态
// 如果 x 处于执行态,用相同的值执行 promise
// 如果 x 处于拒绝态,用相同的据因拒绝 promise
x.then(y=>{
MyPromise.resolveMyPromise(promise2, y, resolve, reject);
}, r=>{
reject(r);
})
}
// 如果 x 为对象或者函数 (x !== null 避免null也为object)
else if( (x!==null && typeof x === "object") || typeof x ==="function"){
try{ // 防止调用 then 的 getter 方法调用抛出异常
// 把 x.then 赋值给 then
const then = x.then;
// 如果 then 是函数,将 x 作为函数的作用域 this 调用之。
// 传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise:
if(typeof then === "function"){
then.call(x, y=>{
// 如果 resolvePromise 和 rejectPromise 均被调用,
// 或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
if(called) return;
called = true;
MyPromise.resolveMyPromise(promise2, y, resolve, reject);
}, r=>{
if(called) return;
called = true;
reject(r);
})
}else{
if(called) return;
called = true;
resolve(x);
}
//
}catch(e){
if(called) return;
called = true;
reject(e);
}
}else{
// 如果不是 MyPromise 或者不是具有 thenable 的类及方法,就直接传入 x 数据
resolve(x);
}
}
再在 then 方法里面调用
then(OnFulfilled, OnRejected) {
if(typeof OnFulfilled !== "function"){
// 如果 OnFulfilled 不是函数或者为空就支持链式调用 .then().then()
OnFulfilled = function(value){
return value;
}
}
if(typeof OnRejected !== "function"){
OnRejected = function(reason){
throw reason;
}
}
let promise2 = new MyPromise((resolve, reject) => {
// promise 执行成功后回调
if (this.state === MyPromise.FULFILLED) {
setTimeout(() => {
// 上一个 Promise回调函数返回的值交给下一个 Promise调用
try{
const x = OnFulfilled(this.value);
MyPromise.resolveMyPromise(promise2, x, resolve, reject);
}catch(e){
reject(e);
}
})
}
// promise 执行失败后回调
if (this.state === MyPromise.REJECTED) {
setTimeout(() => {
try{
const x = OnRejected(this.reason);
MyPromise.resolveMyPromise(promise2, x, resolve, reject);
}catch(e){
reject(e);
}
})
}
// 异步执行函数状态为 Pending 时的处理
if (this.state === MyPromise.PENDING) {
this.OnFulfilledCallbacks.push(
(value) => setTimeout(() => {
try{
const x = OnFulfilled(value);
MyPromise.resolveMyPromise(promise2, x, resolve, reject);
}catch(e){
reject(e);
}
}));
this.OnRejectedCallbacks.push(
(reason) => setTimeout(() => {
try{
const x = OnRejected(reason);
MyPromise.resolveMyPromise(promise2, x, resolve, reject);
}catch(e){
reject(e);
}
}));
}
});
return promise2;
}
- 安装 promises-aplus-tests 测试脚本
npm install promises-aplus-tests -D
npx promises-aplus-tests promise.js
- 在我们 MyPromise.js 文件夹末尾添加测试 deferred 钩子
MyPromise.deferred = function () {
const defer = {}
defer.promise = new MyPromise((resolve, reject) => {
defer.resolve = resolve
defer.reject = reject
})
return defer
}
module.exports = MyPromise;
- 运行代码测试
npx promises-aplus-tests MyPromise.js