面试官最爱问的 Promise 原理

面试官最爱问的 Promise 原理

Promise 是现代前端开发中使用频率非常高的一种异步编程手段。在面试中,面试官通常会询问 Promise 的使用以及其原理。因此,掌握 Promise 的使用和原理是每一位前端开发者必备的技能。

Promise 的基本使用

Promise 是一个异步编程解决方案,利用 Promise 可以更加优雅地解决异步编程中的回调地狱问题。下面是 Promise 的基本使用方式。

const promise = new Promise((resolve, reject) => {
  // 执行异步操作
  const result = doAsyncWork();
  if (result) {
    resolve(result); // 异步操作成功
  } else {
    reject("Error: 操作失败!"); // 异步操作失败
  }
});

// Promise 的 then 方法可以用于处理异步操作成功的情况
promise.then((result) => {
  // 处理异步操作成功的返回值
}).catch((reason) => {
  // 处理异步操作失败的原因
});

在 Promise 的构造函数中,被传入的函数有两个参数:resolve 和 reject。当异步操作执行成功时,应该调用 resolve 方法,并将异步操作的结果作为参数传入。如果异步操作执行失败,则应该调用 reject 方法,并将错误信息作为参数传入。在代码中,Promise 的 then 方法可以用于处理异步操作成功的情况。当异步操作执行成功时,then 方法会被调用,并将异步操作的结果作为参数传入。Promise 的 catch 方法则用于处理异步操作失败的情况。如果异步操作执行失败,则 catch 方法会被调用,并将错误信息作为参数传入。

Promise 的内部实现原理

Promise 的内部实现原理是 Promise A+ 规范所制定的。这个规范定义了 Promise 的行为和接口,以及 Promise 对象的非常详细的内部实现规则和流程。

Promise 内部实现原理的基本流程

在 Promise 内部实现中,Promise 会被分为三个状态:pending、fulfilled 和 rejected。在 Promise 实例化时,Promise 的状态为 pending 状态。当调用 Promise 的 resolve 方法时,Promise 的状态将被改变为 fulfilled 状态,同时触发 Promise 的 then 方法。当调用 Promise 的 reject 方法时,Promise 的状态将被改变为 rejected 状态,同时触发 Promise 的 catch 方法。

同时,Promise 内部还有一个数组用于存储 then 方法中的回调函数。当状态改变后,Promise 会依次执行数组中存储的回调函数。

Promise 内部实现原理的代码展示

下面是一个简单的 Promise 实现,用于演示内部实现原理。

// 实现一个简单版的 Promise
class MyPromise {
  // 构造函数
  constructor(executor) {
    this.status = "pending"; // 初始状态为 pending
    this.value = undefined; // 成功状态的返回值
    this.reason = undefined; // 失败状态的原因
    this.callbacks = []; // 存储 then 方法中的回调函数

    // 执行异步操作
    const resolve = (value) => {
      if (this.status === "pending") {
        this.status = "fulfilled";
        this.value = value;
        this.callbacks.forEach((callback) => callback());
      }
    };

    const reject = (reason) => {
      if (this.status === "pending") {
        this.status = "rejected";
        this.reason = reason;
        this.callbacks.forEach((callback) => callback());
      }
    };

    // 执行 executor 函数
    try {
      executor(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }

  // then 方法
  then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (value) => value;
    onRejected = typeof onRejected === "function" ? onRejected : (reason) => { throw reason; };

    const promise2 = new MyPromise((resolve, reject) => {
      if (this.status === "fulfilled") {
        setTimeout(() => { // 使用 setTimeout 实现异步
          try {
            const x = onFulfilled(this.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      } else if (this.status === "rejected") {
        setTimeout(() => { // 使用 setTimeout 实现异步
          try {
            const x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      } else {
        this.callbacks.push(() => {
          setTimeout(() => { // 使用 setTimeout 实现异步
            try {
              const x = onFulfilled(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch (error) {
              reject(error);
            }
          });
        });
        this.callbacks.push(() => {
          setTimeout(() => { // 使用 setTimeout 实现异步
            try {
              const x = onRejected(this.reason);
              resolvePromise(promise2, x, resolve, reject);
            } catch (error) {
              reject(error);
            }
          });
        });
      }
    });

    return promise2;
  }

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

// 解析 Promise 返回值
function resolvePromise(promise2, x, resolve, reject) {
  if (promise2 === x) {
    return reject(new TypeError("循环引用"));
  }

  let called = false;

  if (x instanceof MyPromise) {
    x.then((value) => {
      resolvePromise(promise2, value, resolve, reject);
    }, reject);
  } else if (x !== null && (typeof x === "object" || typeof x === "function")) {
    try {
      const then = x.then;
      if (typeof then === "function") {
        then.call(x, (value) => {
          if (called) return;
          called = true;
          resolvePromise(promise2, value, resolve, reject);
        }, (reason) => {
          if (called) return;
          called = true;
          reject(reason);
        });
      } else {
        if (called) return;
        called = true;
        resolve(x);
      }
    } catch (error) {
      if (called) return;
      called = true;
      reject(error);
    }
  } else {
    resolve(x);
  }
}

在上面的代码中,Promise 的 then 方法封装了一个新的 Promise 并返回。当 Promise 的状态发生变化时,then 方法中存储的回调函数会被依次执行。同时,resolvePromise 方法用于解析 Promise 的返回值。当 Promise 的返回值为 Promise 时,resolvePromise 方法会递归地解析 Promise 的返回值,以确保 Promise 的状态正确传递。

Promise 的使用技巧

  1. Promise 的链式调用

Promise 提供了一种简洁优雅的链式调用方式,代码的可读性和可维护性更好。使用 Promise 的 then 方法时,需要通过 return 返回一个新的 Promise 对象以便进行链式调用。

getUserData()
  .then(getUserInfo)
  .then((userInfo) => {
    // 处理用户数据
    return generateUserReport(userInfo);
  })
  .then(sendUserReport)
  .catch((error) => {
    console.error(error);
  });
  1. Promise 的 all 方法

Promise 的 all 方法可以用于处理多个 Promise 并发执行的情况。当多个 Promise 对象一起执行时,可以使用 Promise 的 all 方法等待所有 Promise 执行完毕后再执行其他操作。

Promise.all([
  doAsyncWork1(),
  doAsyncWork2(),
  doAsyncWork3(),
]).then((results) => {
  // 处理所有异步操作的结果
}).catch((error) => {
  console.error(error);
});
  1. Promise 的 race 方法

Promise 的 race 方法可以用于处理多个 Promise 并发执行的情况。当多个 Promise 对象一起执行时,可以使用 Promise 的 race 方法等待任意一个 Promise 执行完毕后再执行其他操作。

Promise.race([
  doAsyncWork1(),
  doAsyncWork2(),
  doAsyncWork3(),
]).then((result) => {
  // 处理第一个异步操作的结果
}).catch((error) => {
  console.error(error);
});

总结

在前端开发中,Promise 是一种非常重要的异步编程解决方案。掌握 Promise 的使用方法和内部实现原理对于前端开发者来说都具有重要意义。同时,熟练掌握 Promise 的使用技巧可以更好地提高代码的可读性和可维护性。

来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/6590d6ddeb4cecbf2d61c780


纠错
反馈