手写一个Promise

74 阅读8分钟

1、什么是Promise

Promise 是 JavaScript 中的一种异步编程解决方案。它可以用来处理异步操作的结果,并在操作完成后返回结果。

Promise 对象有三种状态:pending、fulfilled 和 rejected。

  • pending 状态表示操作尚未完成
  • fulfilled 状态表示操作已成功完成
  • rejected 状态表示操作已失败

Promise 对象可以使用 then 方法来处理 fulfilled 状态和 rejected 状态。

Promise 主要解决的是 JavaScript 中的回调地狱问题。回调地狱是指在 JavaScript 中,由于异步操作的需要,经常需要在回调函数中嵌套调用其他回调函数,形成一个复杂的嵌套结构,导致代码难以维护和阅读。

function callbackHell() {
	asyncOperation1(function (result1) {
		asyncOperation2(result1, function (result2) {
			asyncOperation3(result2, function (result3) {
				// do something with result3
			});	
		});
	});
}

这段代码中,每个 asyncOperation 方法都需要一个回调函数来处理结果,而且回调函数嵌套在一起,形成了回调地狱。

Promise 可以通过将回调函数封装到 Promise 对象中,并使用 then 方法来处理结果,使得代码更加简洁和易读。

function promiseHell() {
	asyncOperation1()
	.then(result1 => asyncOperation2(result1))
	.then(result2 => asyncOperation3(result2))
	.then(result3 => {
		// do something with result3
	});
}

这段代码中,每个 asyncOperation 方法返回一个 Promise 对象,并使用 then 方法来处理结果。这样就避免了回调地狱的问题,代码更加简洁易读。

2、Promise规范

Promise 是 JavaScript 中异步编程的一种规范,其规范定义了一组 API,可以用来处理异步操作的结果。

Promise 规范定义了以下几个重要的概念:

  • Promise 对象:表示一个异步操作的结果。
  • resolve 函数:表示异步操作成功完成。
  • reject 函数:表示异步操作失败。
  • then 方法:用来处理 resolve 函数和 reject 函数的结果。
  • catch 方法:用来处理 reject 函数的结果。
  • all():将多个 Promise 对象组成的数组传入 all() 方法,当所有 Promise 都 fulfilled 时,调用 then() 注册的回调函数;如果其中有任意一个 Promise rejected,则调用 catch() 注册的回调函数。
  • race():将多个 Promise 对象组成的数组传入 race() 方法,当其中任意一个 Promise fulfilled 或 rejected 时,立即调用 then() 或 catch() 注册的回调函数。
  • finally():不管 Promise 最终状态是 fulfilled 还是 rejected,都会调用 finally() 注册的回调函数。

Promise 规范是由 ECMAScript 委员会制定的,并在 ECMAScript 2015 (ES6) 中正式被提出。现在几乎所有的浏览器都已经支持了这个规范。

3、Promise基本结构

Promise 的基本结构主要由两部分组成:Promise 对象和 executor 函数。

  • Promise 对象:表示一个异步操作的结果。Promise 对象有三种状态:pending、fulfilled 和 rejected。

  • Executor 函数:用来执行异步操作,并接收 resolve 和 reject 两个函数作为参数。

一般来说,Promise 的基本结构如下:

const promise = new Promise((resolve, reject) => {
	// do something asynchronous which eventually calls either:
	//
	//   resolve(someValue); // fulfilled
	// or
	//   reject("failure reason"); // rejected
});

Promise 对象有三种状态,分别为pending、fulfilled 和 rejected。

  • Pending:表示异步操作还未完成

  • Fulfilled:表示异步操作成功完成

  • Rejected:表示异步操作失败

当 executor 函数中调用 resolve 函数时,Promise 状态变为 fulfilled,当调用 reject 函数时,Promise 状态变为 rejected。

通过 then() 和 catch() 方法可以注册回调函数来处理结果,当Promise状态为 fulfilled 时,调用 then()注册的回调函数,当Promise状态为 rejected 时,调用 catch()注册的回调函数。

// Promise是通过构造函数实例化一个对象,然后通过实例对象上的then方法,来处理异步返回的结果
// 有三种状态,分别为pending、fulfilled 和 rejected
class MyPromise {
  constructor(executor) {
    this.state = 'pending';
    this.value = undefined; //成功结果

    // 当Promise对象已经由等待态(Pending)改变为执行态(Fulfilled)或者拒绝态(Rejected)后,就不能再次更改状态,且终值也不可改变
    const resolve = (value) => {
      // 只能是pending状态的时候才能更改状态
      if (this.state === 'pending') {
        this.state = 'fulfilled';
        this.value = value;
      }
    };

    const reject = (reason) => {
      // 只能是pending状态的时候才能更改状态
      if (this.state === 'pending') {
        this.state = 'rejected';
        this.value = reason;
      }
    };

    // executor函数本身也会可能存在异常,因此通过try/catch来捕获一下异常情况:
    try {
      executor(resolve, reject);
    } catch (e) {
      reject(e);
    }
  }

  then(onFulfilled, onRejected) {}

}

4、then实现

then(onFulfilled, onRejected) {
	//如果 onFulfilled 或 onRejected 不是函数,则忽略它们
	onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
	onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason };

	if (this.status === 'fulfilled') {
		try {
				onFulfilled(this.value);
		} catch (e) {
				reject(e);
		}
	}

	if (this.status === 'rejected') {
		try {
				onRejected(this.value);
		} catch (e) {
				reject(e);
		}
	}

	if (this.status === 'pending') {
			this.onFulfilledCallbacks.push(() => {
				try {
						onFulfilled(this.value);
				} catch (e) {
						reject(e);
				}
			});

			this.onRejectedCallbacks.push(() => {
				try {
						onRejected(this.reason);
				} catch (e) {
						reject(e);
				}
			});
		});
	}
};

then 方法的实现中首先判断当前 Promise 对象的状态,如果已经 fulfilled,就立即调用 onFulfilled 函数处理结果;如果已经 rejected,就立即调用 onRejected 函数处理错误;如果状态是 pending,就将 onFulfilled 和 onRejected 函数分别加入回调队列,等待状态改变时调用。

5、异步实现

Promise 内部实现异步的原理主要是通过 JavaScript 的事件循环机制实现的。在 JavaScript 中,所有的代码都是在单线程的环境下运行的,也就是说不能同时进行多个任务。为了解决这个问题,JavaScript 提供了事件循环机制,用于管理异步任务。

当执行一个异步任务时,JavaScript 会将该任务放入一个队列中,等待主线程执行。当主线程空闲时,它会从队列中取出一个任务并执行。这样,就可以在不阻塞主线程的情况下执行异步任务。

Promise 内部也是基于这个机制实现的。当我们调用 Promise 的 then 方法时,会将成功回调和失败回调分别放入成功队列和失败队列中,等待主线程执行。当 Promise 的状态变为 resolved 或 rejected 时,主线程会从相应的队列中取出回调并执行。这样就可以保证在执行回调时不会阻塞主线程。

class MyPromise {
	constructor(executor) {
		this.state = 'pending';
		this.value = undefined;
		this.onFulfilledCallbacks = []; //成功的回调
		this.onRejectedCallbacks = []; //失败的回调

		const resolve = (value) => {
			if (this.state === 'pending') {
				this.state = 'fulfilled';
				this.value = value;
				this.onFulfilledCallbacks.forEach((cb) => cb());
			}
		};

		const reject = (reason) => {
			if (this.state === 'pending') {
				this.state = 'rejected';
				this.value = reason;
				this.onRejectedCallbacks.forEach((cb) => cb());
			}
		};

		try {
			executor(resolve, reject);
		} catch (e) {
			reject(e);
		}
	}

	then(onFulfilled, onRejected) {
		// ...省略
		if (this.status === 'pending') {
			this.onFulfilledCallbacks.push(() => {
				try {
					onFulfilled(this.value);
				} catch (e) {
					reject(e);
				}
			});

			this.onRejectedCallbacks.push(() => {
				try {
					let x = onRejected(this.reason);
					resolvePromise(promise2, x, resolve, reject);
				} catch (e) {
					reject(e);
				}
			});
		}
	}
}

6、链式调用实现

可以在 then 方法中返回一个新的 Promise 对象来实现链式调用。

class MyPromise {
 // 省略...

  then(onFulfilled, onRejected) {
    if (this.state === 'fulfilled') {
        try {
          const x = onFulfilled(this.value);
          resolvePromise(x, resolve, reject);
        } catch (e) {
          reject(e);
        }
      }

      if (this.state === 'rejected') {
        try {
          const x = onRejected(this.value);
          resolvePromise(x, resolve, reject);
        } catch (e) {
          reject(e);
        }
      }

      if (this.state === 'pending') {
        this.onFulfilledCallbacks.push(() => {
          try {
            const x = onFulfilled(this.value);
            resolvePromise(x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        });
        this.onRejectedCallbacks.push(() => {
          try {
            const x = onRejected(this.value);
            resolvePromise(x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        });
      }
    return new MyPromise((resolve, reject) => {});
  }
}

上面的代码中,在 then 方法中返回了一个新的 MyPromise 对象。

在 onFulfilled 和 onRejected 回调函数中,我们调用 resolvePromise 函数来处理返回值 x。

  1. 如果 x 是一个普通值,则直接调用 resolve(x)

  2. 如果 x 是另一个 promise 对象,则继续调用它的 then 方法,直到返回一个普通值为止。

7、resolvePromise实现

resolvePromise 是一个函数,用于处理将一个 promise 作为 then 方法返回值的情况。这是为了遵循 Promise A+ 规范中关于 promise 链式调用的要求。

具体来说,当一个 promise 的 then 方法调用时,如果返回值是一个 promise,那么这个 promise 的状态就会传递给当前 promise。这个过程称为 promise 链式调用。

resolvePromise 函数的实现原理如下:

  1. 检查 x 是不是 promise。如果是,则使用 x 的状态来更新 promise。

  2. 如果 x 是一个普通的值,则调用 resolve 函数将 x 作为结果来完成 promise。

  3. 如果 x 是一个函数或者是一个对象,则调用 x.then 方法,并将 promise 的 resolve 和 reject 作为参数传入。

  4. 如果 x.then 方法抛出了异常,则调用 promise 的 reject 函数。

举个例子,当我们调用 promise.then(resolvePromise) 时,会在resolvePromise 中接收到 promise的resolve 和 reject 函数,在函数里面根据x的类型进行不同的操作。

这个 resolvePromise 是为了解决promise链式调用时,后面then方法返回的promise状态如何传递到前面promise的问题,从而遵循Promise A+规范。

function resolvePromise(promise, x, resolve, reject) {
  // 如果promise和x是同一个对象,抛出类型错误
  if (promise === x) {
    return reject(new TypeError('Chaining cycle detected for promise'));
  }
  let called = false;

  // 如果x是promise对象
  if (x instanceof Promise) {
    // 如果x的状态是pending,将promise的状态也设置为pending
    if (x.status === 'pending') {
      x.then(function(value) {
        resolvePromise(promise, value);
      }, function(reason) {
        reject(promise, reason);
      });
    } else {
      // 否则将promise的状态和x的状态保持一致
      promise.status = x.status;
      promise.value = x.value;
      promise.resolveCallbacks.forEach(cb => cb(promise.value));
    }
  } else if (x && (typeof x === 'object' || typeof x === 'function')) {
    // 如果x是一个thenable
    try {
      let then = x.then;
      if (typeof then === 'function') {
        then.call(x, function (y) {
          if (called) return;
          called = true;
          resolvePromise(promise, y, resolve, reject);
        }, function (r) {
          if (called) return;
          called = true;
          reject(r);
        });
      } else {
        resolve(x);
      }
    } catch (e) {
      if (called) return;
      called = true;
      reject(e);
    }
  } else {
    resolve(x);
  }
}

8、完整代码

 class MyPromise {
    constructor(executor) {
      this.state = 'pending'; // promise状态
      this.value = undefined; // 返回值
      this.onFulfilledCallbacks = []; //成功的回调
      this.onRejectedCallbacks = []; // 失败的回调
      
      const resolve = (value) => {
        if(this.state === 'pending') {
          this.state = 'fulfilled';
          this.value = value;
          this.onFulfilledCallbacks.forEach((cb) => cb());
        }
      };
      
      const reject = (reason) => {
        if(this.state === 'pending') {
          this.state = 'rejected';
          this.value = reason;
          this.onRejectedCallbacks.forEach((cb) => cb());
        }
      };
      
      try {
        executor(resolve, reject);
      } catch(e) {
        reject(e);
      }
    }
    
    then(onFulfilled, onRejected) {
      
      let promise2 = new MyPromise((resolve, reject) => {
        if(this.state === 'fulfilled') {
          setTimeout(() => {
            try {
              const x = onFulfilled(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch(e) {
              reject(e);
            }
          })
        }
        
        if(this.state === 'rejected') {
          setTimeout(() => {
            try {
              const x = onRejected(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch(e) {
              reject(e);
            }
          })
        }
        
        if(this.state === 'pending') {
          this.onFulfilledCallbacks.push(() => {
            setTimeout(() => {
              try {
                const x = onFulfilled(this.value);
                resolvePromise(promise2, x, resolve, reject);
              } catch(e) {
                reject(e);
              }
            })
          });
          
          this.onRejectedCallbacks.push(() => {
            setTimeout(() => {
              try {
                const x = onRejected(this.value);
                resolvePromise(promise2, x, resolve, reject);
              } catch(e) {
                reject(e);
              }
            })
          });
        }
      });
      
      return promise2
    }
  }
  
  function resolvePromise(promise, x, resolve, reject) {
  // 如果promise和x是同一个对象,抛出类型错误
  if (promise === x) {
    return reject(new TypeError('Chaining cycle detected for promise'));
  }
  let called = false;

  // 如果x是promise对象
  if (x instanceof Promise) {
    // 如果x的状态是pending,将promise的状态也设置为pending
    if (x.status === 'pending') {
      x.then(function(value) {
        resolvePromise(promise, value);
      }, function(reason) {
        reject(promise, reason);
      });
    } else {
      // 否则将promise的状态和x的状态保持一致
      promise.status = x.status;
      promise.value = x.value;
      promise.resolveCallbacks.forEach(cb => cb(promise.value));
    }
  } else if (x && (typeof x === 'object' || typeof x === 'function')) {
    // 如果x是一个thenable
    try {
      let then = x.then;
      if (typeof then === 'function') {
        then.call(x, function (y) {
          if (called) return;
          called = true;
          resolvePromise(promise, y, resolve, reject);
        }, function (r) {
          if (called) return;
          called = true;
          reject(r);
        });
      } else {
        resolve(x);
      }
    } catch (e) {
      if (called) return;
      called = true;
      reject(e);
    }
  } else {
    resolve(x);
  }
}