Promise() 构造函数

Promise() 构造函数 可以创建 Promise 对象。主要用来封装一些基于回调的 API,这些 API 均未支持 Peomise。

语法

new Promise(executor)

注意: Promise() 只能通过 new 运算符来构造。如果尝试在没有使用 new 的情况下调用 Promise() ,将会抛出 TypeError 异常 。

参数

executor

在构造函数中执行的 function。它接收两个参数,都是函数: resolveFunc 和 rejectFunc。

executor 中抛出任何错误都会导致 Promise 的返回值被忽略并且 Promise 被拒绝。

返回值

当通过 new 关键字调用 Promise 构造函数时,会返回一个 Promise 对象。当 resolveFunc 或者 rejectFunc 被调用时,该 Promise 对象就会变成 resolved 状态。

注意:当 resolveFunc 或者 rejectFunc 传入的参数是另一个 Promise 时,构造的新 Promise 对象将是 resolved 状态,但不是 settled 状态。

描述

在 Promise 之前,异步任务都基于回调函数实现。

readFile("./data.txt", (error, result) => {
  // 这个回调函数将在任务完成后被调用,返回最终的 `error` 或 `result`。
  // 任何依赖于返回结果的操作都必须在这个回调函数内定义。
});
// `readFile` 请求被发出后,此处的代码会立即执行。
// 它不会等待回调函数被调用,因此使 `readFile` 成为了“异步”的。

Promise() 构造函数允许将基于回调的 API 转换为基于 Promise 的 API,使得 Promise 具有更好的可读性和语言特性。

注意: 如果任务已经基于 Promise 实现,大概率将用不到 Promise() 构造函数。

executeor 是一段自定义代码,可以将回调函数的结果和 Promise 关联在一起。

function executor(resolveFunc, rejectFunc) {
  // 通常,`executor` 函数用于封装某些接受回调函数作为参数的异步操作,比如上面的 `readFile` 函数
}

resolveFunc 和 rejectFunc 也是函数,他们接受任意类型的参数。

resolveFunc(value); // 解决时调用
rejectFunc(reason); // 拒绝时调用

resolveFunc 的参数 value 可以是另一个 Promise 对象,新构造的 Promise 对象的状态将 lock-in 到传入的 Promise 对象。

rejecteFunc 的语义类似 throw 语句,因此其参数 reason 通常是一个 Error 实例。

如果 resolveFunc 或者 rejectFunc 的参数被省略了,那么新构造的 Promise 对象的状态将会是 fulfilled 或者 rejected ,但是 undefined。

executor 的完成状态对新构造的 Promise 的状态影响有限:

  • executor 函数的返回值会被忽略。executor 函数中的 return 语句仅影响控制流程,能够影响函数的某个部分是否能够执行,但不会影响 Promise 的 fulfilled 值。如果 executor 函数 return 了,而且未来不可能调用 resolveFunc 或者 rejectFunc (比如:未安排异步任务),则 Promise 将永远保持 pending 状态。
  • 如果在 executor 函数中抛出错误,则 Promise 将被拒绝,除非 resolveFunc 或者 rejectFunc 已经被调用。

注意: Promise 的 pending 状态不会阻止程序退出。如果时间循环为空,即使 Promise 是 pending 状态(因为这个 Promise 必然是永远处于 pending 状态),程序也会退出。

典型的 Promise 流程:

  1. 在构造函数生成新的 Promise 对象时,会生成一对相应的 resolveFunc 和 rejectFunc 函数,他们与 Promise 对象绑定在一起。
  2. executor 一般用于封装异步操作,并且这些异步操作都提供基于回调的 API。回调函数(传给原始回调 API 的函数)在 executor 代码中定义,因此这些回调函数可以访问 resolveFunc 和 rejectFunc。
  3. execuotr 是同步调用的(在构造 Promise 时立即调用),并将 resolveFunc 和 rejectFunc 函数作为参数传入。
  4. executor 中的代码有机会执行某些操作:异步任务的最终完成会通过 resolveFunc 和 rejectFunc 引起的副作用与 Promise 实例进行通信。这个副作用将 Promise 对象变为 resolved 状态。
    • 如果先调用了 resolveFunc ,则传入的值将 resolve。新构造的 Promise 可能有三种状态:pending (如果传入了另一个 thenable 对象),fulfilled (如果传入的是一个非 thenable 的值,大部分情况下是 fulfilled),rejected (解析值无效)。
    • 如果先调用 rejectFunc,Promise 立刻变为 rejected 状态。
    • 一旦 resolveFunc 或者 rejectFunc 中的任意一个函数被调用,Promise 将保持 resolved 状态。并且只有第一次调用这两个函数的时候会影响 Promise 的最终状态,在这之后对 resolveFunc 或者 rejectFunc 的任何调用都不能改变新 Promise 的状态和值。
    • 如果 executor 抛出错误,则 Promise 被拒绝。但是如果 resolveFunc 或者 rejectFunc 之一已经被调用过(即 Promise 的状态已经是 resolved),该错误将被忽略。
    • 新构造的 Promise 处于 resolved 状态,不一定处于 fulfilled 或者 rejected 两个状态之一。(传入一个 thenable 对象的情况下,新构造的 Promise 的状态将 lock-in 于传入的 thenable 对象并且与 resolved 状态的 thenable 对象一致)
  5. Promise 处于 settled 状态时,就可以(异步地)调用任何通过 then(),catch(),finally() 关联的进一步的处理程序。最终的 fulfilled 的值或者 rejected 的原因在调用时会作为输入的参数,传给 fulfilled 程序和 rejected 程序。
const readFilePromise = (path) =>
  new Promise((resolve, reject) => {
    readFile(path, (error, result) => {
      if (error) {
        reject(error);
      } else {
        resolve(result);
      }
    });
  });

readFilePromise("./data.txt")
  .then((result) => console.log(result))
  .catch((error) => console.error("读取文件失败"));

resolve 和 reject 回调仅在 executor 函数的作用域内可用,即在构造了 promise 之后无法再访问。如果需要先构造再写回调,可以用 Promise.withResolvers() 方法,这个方法暴露了 resolve 和 reject 函数。

resolve 函数

resolve 函数:

  • 如果调用时传入了新构造的这个 Promise 本身(即 resolve 绑定的 Promise 对象),则 Promise 对象会成为 rejected 并抛出一个 TypeError 错误。
  • 如果传入了一个非 thenable 的值(基本类型,或者一个没有 then 属性,或者一个 then 属性不可调用的对象),则该 Promise 对象会立即以该值转变为 fulfilled。
  • 如果传入了一个 thenable 对象(包括一个 Promise 实例),则该 thenable 对象的 then 方法将被保存并在将来被调用(异步调用)。then 方法将被调用并传入两个回调函数,这两个函数的行为与传递给 executor 函数的 resolveFunc 和 rejectFunc 函数完全相同。如果调用 then 方法时出现错误,则当前新构造的 Promise 对象会 rejected 并抛出这个错误。
new Promise((resolve, reject) => {
  resolve(thenable);
});

相当于:

new Promise((resolve, reject) => {
  try {
    thenable.then(
      (value) => resolve(value),
      (reason) => reject(reason),
    );
  } catch (e) {
    reject(e);
  }
});

在 resolve(thenable) 的情况中:

  1. resolve 函数时同步调用的,因此再次调用 resolve 或 reject 函数没有任何影响,即使通过 anotherPromise.then() 绑定的处理程序尚未被调用。
  2. then 方法是异步调用的,因此如果传入 thenable 兑现那个,则该 Promise 对象不会立即 resolved。

因为 resolve 函数再次调用时使用 thenable.then() 传递的值作为参数,所以 resolve 函数可以展开嵌套的 thenable 对象,其中一个 thenable 对象调用其 onFulfilled 处理程序并返回另一个 thenable 对象。这种情况下真实的 Promise 对象的 fulfilled 处理器永远不会接收到 thenable 对象作为其 fulfiiled 的值。

示例

将基于回调的 API 转换为基于 Promise 的 API

在适当的时候调用 resolve 和 reject 函数,并返回一个 Promise 对象,可以让一个函数具有 Promise 的功能。

function myAsyncFunction(url) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open("GET", url);
    xhr.onload = () => resolve(xhr.responseText);
    xhr.onerror = () => reject(xhr.statusText);
    xhr.send();
  });
}

调用 resolveFunc 的效果

const pendingResolved = new Promise((resolveOuter, rejectOuter) => {
  resolveOuter(
    new Promise((resolveInner) => {
      setTimeout(() => {
        resolveInner("内部");
      }, 100);
    }),
  );
});

pendingResolved Promise 对象在创建时就已经是 resolved,因为它已经 lock-in 以匹配内部 Promise 对象的最终状态,后续在 executor 函数中调用 resolveOuter 或 rejectOuter 或抛出错误对其最终状态没有影响。然而,内部 Promise 对象仍然处于 pending 状态,直到 100 毫秒后才是 resolved,因此外部 Promise 对象也处于 pending 状态。

const fulfilledResolved = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("外部");
  }, 100);
});

fulfilledResolved Promise 对象在 resolved 时立即变为 fulfilled 状态,因为它以非 thenable 值 resolve。然而,在它被创建时,它是未解决的,因为 resolve 或 reject 函数还没有被调用。未解决的 Promise 对象必然是 pending 状态。

// 1. 传入 Promise 本身
const rejectedResolved1 = new Promise((resolve) => {
  // 注意:resolve 必须异步调用,以便初始化 rejectedResolved1 变量
  setTimeout(() => resolve(rejectedResolved1)); // TypeError: Chaining cycle detected for promise #<Promise>
});

// 2. 传入一个在访问 `then` 属性时抛出异常的对象
const rejectedResolved2 = new Promise((resolve) => {
  resolve({
    get then() {
      throw new Error("无法访问 then 属性");
    },
  });
});

调用 rejectFunc 函数会导致 Promise 对象 rejected。然而,即使在调用 resolveFunc 回调函数时,也有两种方法可以使 Promise 对象立即 rejected。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值