likes
comments
collection
share

面试官:请你解释一下Promise工作原理

作者站长头像
站长
· 阅读数 28

今天我们继续讲promise,本文主要包含对promise中resolve,reject和.then函数的介绍,最后通过对上述函数的理解手写出Promise源码。


Promise

当创建一个新的Promise实例时,构造函数会立即执行一个传递给它的executor函数,该函数接受两个参数:resolvereject。这两个参数实际上是函数,用于改变Promise的状态。resolve用于在异步操作成功完成时调用,而reject则在异步操作失败时调用。

   - 拥有三种状态为 pending fulfilled rejected
   - 实例化一个 promise 会得到一个状态为 pending 的实例对象
   - 该对象可以访问 promise 原型上的 then 方法
   - 当该对象中的状态没有变更为 fulfilled,then 接收到的回调是不触发的

resolve

resolve函数的作用

resolve函数被用来解析一个Promise,使其从pending状态变为fulfilled(已解决)状态。

示例:

function a() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('a is ok');
      resolve()//如果我们不写resolve,.then里的逻辑是不生效的
    }, 1000)
  })
}

function b() {
  console.log('b is ok');
}

a()
  .then(
    () => {
      b()
      })

面试官:请你解释一下Promise工作原理

如果不加 resolve(), 运行结果不会运行b()

面试官:请你解释一下Promise工作原理

resolve被调用时,它还可以接受一个参数,这个参数会被传递给.then()方法中第一个回调函数作为其参数


new Promise((resolve, reject) => {
 // 异步操作完成后
  resolve(1); // 传递一个值1给resolve
});

.then(
    (res) => {
      console.log(res);
      b()
      })

面试官:请你解释一下Promise工作原理

.then()方法的使用

.then()方法允许你注册回调函数,这些函数会在Promise变为fulfilled状态时执行。如果你在resolve中传递了一个值,那么这个值会被作为.then()方法的第一个回调函数的参数。

例如:

new Promise((resolve, reject) => {
  resolve(1); // 传递一个值1给resolve
})
.then((value) => {
  console.log(value); // 输出1
});

如果没有调用resolvePromise将保持在pending状态,因此任何.then()方法中的回调都不会被执行。

  • 如果resolve中带了值,那么.then()中的第一个回调函数将接收这个值作为参数。

.then()里的第二个回调完全等同于.catch()

Promise.then()方法中,可以传递两个回调函数:第一个是当Promise成功(即resolved)时调用的,第二个则是当Promise失败(即rejected)时调用的。

但是,从ES6开始,为了更清晰地区分成功和失败的处理逻辑,推荐使用.then()来处理成功的情况,以及单独的.catch()来处理失败的情况。

然而,在语法上,.then()的第二个参数确实可以用来处理rejected状态,这与.catch()是等效的。也就是说,下面两段代码是等价的:

使用.then()的第二个参数:

a()
  .then(
    (res) => {
      console.log(res, 'res');
    },
    (err) => {
      console.log(err, "err");
    }
  );

面试官:请你解释一下Promise工作原理 使用.catch()

a()
  .then((res) => {
    console.log(res, 'res');
  })
  .catch((err) => {
    console.log(err, "err");
  });

面试官:请你解释一下Promise工作原理

在两种情况下,如果Promisereject,则err将会被打印。

但是,使用.catch()使得错误处理更加明显和易于理解,尤其是在链式调用中,它能够明确地表明这是处理错误的地方。

此外,.catch()还可以捕获前面.then()中抛出的异常,而.then()的第二个参数则不能

例如:

a()
  .then(() => {
    throw new Error('An error occurred');
  })
  .catch((err) => {
    console.log(err.message);
  });

在这种情况下,.catch()将捕获在.then()内部抛出的异常。而如果使用.then()的第二个参数,它将无法捕获前面.then()中抛出的异常,除非你再次调用.then()并提供一个新的失败回调。

因此,尽管.then()的第二个参数在功能上与.catch()相似,但.catch()提供了更好的可读性和更全面的错误处理能力。

reject

调用reject函数时,会使Promise进入rejected(拒绝)状态。

一旦Promise处于rejected状态,任何随后的.then()方法将不会被调用,因为它们只处理fulfilled(已解决)的情况

我们可以使用.catch()方法来处理rejected状态。

.catch()实际上是一个简化的.then(null, onRejected)调用,它只关注rejected状态,并且会接收reject函数传入的任何值或错误作为参数。

function a() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('a is ok');
      reject(2) // 这里使用reject

    }, 1000)
  })
}

function b() {
  console.log('b is ok');
}

a()
  .then(
    (res) => {
      console.log(res);
      b()
    })
  .catch(err => {
    console.log(err); // 这里会捕获到reject抛出的值2
  })
    

面试官:请你解释一下Promise工作原理

因为Promise通过reject(2)进入了rejected状态,所以.then()块中的代码不会执行,而.catch()中的代码会被调用来处理错误。

如果在Promise被拒绝后调用.then()而不调用.catch(),那么任何未处理的rejected状态都会导致程序中未捕获的异常,这通常不是我们想要的结果。

因此,确保在可能的rejected情况下使用.catch()来处理错误,或者在.then()之后链式调用.catch()来捕捉任何可能的异常。

总结:

  • 必须调用resolvereject来改变Promise的状态。
  • resolve用于标记异步操作的成功,使Promise变为fulfilled状态。
  • reject用于标记异步操作的失败,使Promise变为rejected状态。
  • .then()用于处理fulfilled状态,而.catch()用于处理rejected状态。

手写Promise

思路

  1. 创建Promise:当使用new Promise()构造函数时,必须传递一个执行器函数executor,这个函数会被立即调用。
  2. executor的参数executor函数接受两个参数:resolvereject。这两个函数由Promise构造函数提供,用于改变Promise的状态。
  3. 状态转换Promise有三种状态:pending(待定)、fulfilled(已解决)和rejected(已拒绝)。状态只能从pending变成fulfilledrejected,且一旦状态改变,就不能再变回pending或改变为另一个状态。
  4. 状态不变性:一旦Promise变为fulfilledrejected,其状态就永久固定,不会再改变。
  5. then方法:每个Promise都有then方法,它接受两个可选的回调函数:onFulfilledonRejected,分别用于处理Promise成功和失败的情况。
  6. then方法的执行:如果在调用thenPromise已经处于fulfilledrejected状态,那么相应的回调onFulfilledonRejected将立即执行。如果Promise仍处于pending状态,那么这些回调将被存储起来,直到状态确定后才执行。
  7. 可选的回调then方法的onFulfilledonRejected参数是可选的,如果省略,then方法将假定一个默认的行为(对于onFulfilled,默认为返回值本身;对于onRejected,默认为抛出错误)。
  8. 链式调用Promisethen方法可以被多次调用,每次调用都返回一个新的Promise,这使得你可以链接多个then方法来构建复杂的异步操作流程。
  9. 传递结果:如果then方法的回调返回一个值,这个值将作为下一个then方法中onFulfilled回调的参数。
  10. 传递错误:如果then方法的回调抛出一个异常,这个异常将作为下一个then方法中onRejected回调的参数。
  11. 处理返回的Promise:如果then方法的回调返回另一个Promise,那么整个链式调用会等待这个新Promise的结果。如果新Promise成功,链式调用继续向下执行;如果新Promise失败,链式调用会跳转到最近的onRejected回调。

完整代码:

class MyPromise {
  constructor(executor) {
    // 初始化状态和值
    this.status = 'pending'; // 当前Promise的状态:pending, fulfilled, 或 rejected
    this.value = null;       // fulfilled时的值
    this.reason = null;      // rejected时的原因
    this.onFulfilledCallbacks = []; // 存储所有成功回调
    this.onRejectedCallbacks = [];  // 存储所有失败回调

    // 解析Promise,改变其状态到fulfilled
    const resolve = value => {
      if (this.status === 'pending') {
        this.status = 'fulfilled';
        this.value = value;
        // 执行所有成功回调
        this.onFulfilledCallbacks.forEach(fn => fn());
      }
    };

    // 拒绝Promise,改变其状态到rejected
    const reject = reason => {
      if (this.status === 'pending') {
        this.status = 'rejected';
        this.reason = reason;
        // 执行所有失败回调
        this.onRejectedCallbacks.forEach(fn => fn());
      }
    };

    // 尝试执行executor,如果抛出错误,自动调用reject
    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; };

    // 返回新的Promise,允许链式调用
    return new MyPromise((resolve, reject) => {
      // 根据当前状态,决定执行哪个回调
      if (this.status === 'fulfilled') {
        setTimeout(() => {
          try {
            // 调用onFulfilled并处理其结果
            const x = onFulfilled(this.value);
            this.resolvePromise(x, resolve, reject);
          } catch (error) {
            // 如果onFulfilled抛出错误,直接拒绝新的Promise
            reject(error);
          }
        }, 0);
      } else if (this.status === 'rejected') {
        setTimeout(() => {
          try {
            // 调用onRejected并处理其结果
            const x = onRejected(this.reason);
            this.resolvePromise(x, resolve, reject);
          } catch (error) {
            // 如果onRejected抛出错误,直接拒绝新的Promise
            reject(error);
          }
        }, 0);
      } else {
        // 如果状态是pending,将回调存储起来,等待状态改变
        this.onFulfilledCallbacks.push(() => {
          setTimeout(() => {
            try {
              const x = onFulfilled(this.value);
              this.resolvePromise(x, resolve, reject);
            } catch (error) {
              reject(error);
            }
          }, 0);
        });

        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              const x = onRejected(this.reason);
              this.resolvePromise(x, resolve, reject);
            } catch (error) {
              reject(error);
            }
          }, 0);
        });
      }
    });
  }

  // 辅助函数,用于处理then方法中返回的值x
  resolvePromise(promise2, x, resolve, reject) {
    // 检查循环引用
    if (promise2 === x) {
      return reject(new TypeError('Chaining cycle'));
    }

    // 检查x的类型
    if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
      let called = false; // 标记x.then是否已经被调用

      try {
        let then = x.then;
        if (typeof then === 'function') {
          // 如果x是一个Promise,调用其then方法
          then.call(x, y => {
            if (called) return;
            called = true;
            this.resolvePromise(promise2, y, resolve, reject);
          }, r => {
            if (called) return;
            called = true;
            reject(r);
          });
        } else {
          // 如果x不是一个Promise,直接解析
          if (called) return;
          called = true;
          resolve(x);
        }
      } catch (error) {
        // 捕获x.then抛出的错误
        if (called) return;
        called = true;
        reject(error);
      }
    } else {
      // 如果x不是对象或函数,直接解析
      resolve(x);
    }
  }
}

module.exports = MyPromise;

以上就是本文的全部内容,通过手写Promise,我们不仅加深了对Promise内部机制的理解,还掌握了如何实现自己的Promise库,这对于进一步学习异步编程和深入JavaScript核心概念有着重要的意义。希望本文对你有所帮助,感谢你的阅读!

转载自:https://juejin.cn/post/7393307982653014054
评论
请登录