返回

Promise原理分析与手写简易版Promise

前端

一、前言:Promise灵魂三问

  1. Promise是什么?

Promise是JavaScript中用来处理异步操作的工具。它是一个对象,表示一个异步操作的最终完成或失败及其结果值。

  1. Promise做什么?

Promise可以用来避免回调函数嵌套带来的“回调地狱”问题。它允许我们将异步操作链式调用,使代码更加简洁和易于理解。

  1. Promise怎么做?

Promise内部采用状态机来管理异步操作的状态。当异步操作完成时,Promise的状态会从“pending”变为“resolved”或“rejected”。状态的改变会触发相应的回调函数,以便我们处理异步操作的结果。

二、Promise的原理剖析

  1. Promise的状态

Promise有三种状态:“pending”、“resolved”和“rejected”。

  • pending: 表示异步操作正在进行中,尚未完成。
  • resolved: 表示异步操作已成功完成,并带有结果值。
  • rejected: 表示异步操作已失败,并带有错误信息。
  1. Promise的回调函数

Promise提供两个回调函数:“then”和“catch”。

  • then: 用于处理异步操作成功完成的情况。
  • catch: 用于处理异步操作失败的情况。
  1. Promise的链式调用

Promise支持链式调用,即一个Promise的“then”回调函数可以返回另一个Promise,这样我们就可以将多个异步操作串联起来,形成一个异步操作链。

三、手写简易版Promise

下面我们来一步步实现一个简易版的Promise:

  1. 定义Promise类
class Promise {
  constructor(executor) {
    this.state = 'pending';
    this.value = undefined;
    this.reason = undefined;
    this.onResolvedCallbacks = [];
    this.onRejectedCallbacks = [];

    const resolve = (value) => {
      if (this.state !== 'pending') return;
      this.state = 'resolved';
      this.value = value;
      this.onResolvedCallbacks.forEach((callback) => callback(value));
    };

    const reject = (reason) => {
      if (this.state !== 'pending') return;
      this.state = 'rejected';
      this.reason = reason;
      this.onRejectedCallbacks.forEach((callback) => callback(reason));
    };

    executor(resolve, reject);
  }

  then(onResolved, onRejected) {
    if (typeof onResolved !== 'function') onResolved = (value) => value;
    if (typeof onRejected !== 'function') onRejected = (reason) => { throw reason; };

    const promise2 = new Promise((resolve, reject) => {
      if (this.state === 'resolved') {
        setTimeout(() => {
          try {
            const x = onResolved(this.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      } else if (this.state === 'rejected') {
        setTimeout(() => {
          try {
            const x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      } else {
        this.onResolvedCallbacks.push(() => {
          setTimeout(() => {
            try {
              const x = onResolved(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              const x = onRejected(this.reason);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
      }
    });

    return promise2;
  }

  catch(onRejected) {
    return this.then(null, onRejected);
  }

  finally(onFinally) {
    return this.then(
      (value) => Promise.resolve(onFinally()).then(() => value),
      (reason) => Promise.resolve(onFinally()).then(() => { throw reason; })
    );
  }

  static resolve(value) {
    return new Promise((resolve) => {
      resolve(value);
    });
  }

  static reject(reason) {
    return new Promise((_, reject) => {
      reject(reason);
    });
  }

  static all(promises) {
    return new Promise((resolve, reject) => {
      const values = [];
      let count = 0;

      promises.forEach((promise, index) => {
        promise.then((value) => {
          values[index] = value;
          count++;
          if (count === promises.length) {
            resolve(values);
          }
        }, reject);
      });
    });
  }

  static race(promises) {
    return new Promise((resolve, reject) => {
      promises.forEach((promise) => {
        promise.then(resolve, reject);
      });
    });
  }
}

function resolvePromise(promise2, x, resolve, reject) {
  if (promise2 === x) {
    reject(new TypeError('Chaining cycle detected for promise!'));
  }

  if ((x !== null && typeof x === 'object') || typeof x === 'function') {
    let called = false;
    try {
      const then = x.then;
      if (typeof then === 'function') {
        then.call(
          x,
          (y) => {
            if (called) return;
            called = true;
            resolvePromise(promise2, y, resolve, reject);
          },
          (r) => {
            if (called) return;
            called = true;
            reject(r);
          }
        );
      } else {
        resolve(x);
      }
    } catch (e) {
      if (called) return;
      called = true;
      reject(e);
    }
  } else {
    resolve(x);
  }
}
  1. 使用方法
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('Hello, world!');
  }, 2000);
});

promise.then((value) => {
  console.log(value); // 'Hello, world!'
}).catch((reason) => {
  console.log(reason);
});

四、Promise与回调函数和事件循环

  1. Promise与回调函数

Promise与回调函数都是用来处理异步操作的,但Promise相比回调函数有以下优点:

  • 可读性更好: Promise的链式调用使得代码更加简洁易读,避免了回调函数嵌套带来的“回调地狱”问题。
  • 易于出错处理: Promise的“catch”回调函数可以方便地处理异步操作失败的情况。
  • 支持链式调用: Promise支持链式调用,可以将多个异步操作串联起来,形成一个异步操作链。
  1. Promise与事件循环

Promise内部使用事件循环来调度异步操作。当一个异步操作完成时,Promise的状态会从“pending”变为“resolved”或“rejected”。状态的改变会触发相应的回调函数,以便我们处理异步操作的结果。

五、结语

Promise是JavaScript中处理异步操作的利器,它可以帮助我们编写更加简洁、易读、易于出错处理的代码。通过对Promise原理的深入剖析和手写简易版Promise的实现,我们对Promise有了更深入的理解。希望本文能帮助你更好地理解和使用Promise,从而提升你的异步编程技能。