Skip to content

Promise

三种状态:

  • pending:进行中;
  • fulfilled:已成功;
  • rejected:已失败;

两个特点:

  • 对象的状态不受外界影响,只有异步操作的结果,可以决定当前是哪一种状态。
  • 一旦状态改变,就不会再变,任何时候都可以得到这个结果。

Thenable

  • 在 Promise 成为 JavaScript 语言的一部分之前,JavaScript 生态系统已经有了多种 Promise 实现。尽管它们在内部的表示方式不同,但至少所有类 Promise 的对象都实现了 Thenable 接口。thenable 对象实现了 .then() 方法,该方法被调用时需要传入两个回调函数,一个用于 Promise 被兑现时调用,一个用于 Promise 被拒绝时调用。Promise 也是 thenable 对象。

  • 为了与现有的 Promise 实现进行交互,JavaScript 语言允许在 Promise 的位置使用 thenable 对象。例如,Promise.resolve 方法不仅可以解析 Promise 对象,还可以追踪 thenable 对象。

javascript
const aThenable = {
  then(onFulfilled, onRejected) {
    onFulfilled({
      // thenable 对象被兑现为另一个 thenable 对象
      then(onFulfilled, onRejected) {
        onFulfilled(42);
      },
    });
  },
};
Promise.resolve(aThenable); // 一个兑现值为 42 的 Promise
const aThenable = {
  then(onFulfilled, onRejected) {
    onFulfilled({
      // thenable 对象被兑现为另一个 thenable 对象
      then(onFulfilled, onRejected) {
        onFulfilled(42);
      },
    });
  },
};
Promise.resolve(aThenable); // 一个兑现值为 42 的 Promise

Promise() 构造函数

javascript
new Promise(executor)
new Promise(executor)
  • 参数:executor

    executor 在构造函数中执行的 function。它接收两个函数作为参数:resolveFunc 和 rejectFunc。

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

  • 返回:当通过 new 关键字调用 Promise 构造函数时,它会返回一个 Promise 对象。

    当 resolveFunc 或者 rejectFunc 被调用时,该 Promise 对象就会变为已解决(resolved)。

    如果调用 resolveFunc 或 rejectFunc 并传入另一个 Promise 对象作为参数,可以说该 Promise 对象“已解决”,但仍未“敲定(settled)”。

  • executor 是将回调函数的结果与 Promise 关联在一起的自定义代码。编写 executor 的工作由程序员完成。

  • resolveFunc 和 rejectFunc 也是函数,你可以给它们任何实际的名称。它们的函数签名很简单:它们接受一个任意类型的参数。

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

传入 resolveFunc 的 value 参数可以是另一个 Promise 对象,在这种情况下,新构造的 Promise 对象的状态将“锁定”到传入的 Promise 对象(作为 resolution Promise 的一部分)。

rejectFunc 的语义类似于 throw 语句,因此 reason 通常是一个 Error 实例。

如果 value 或 reason 中有任意一个被省略,Promise 将会被兑现(fulfilled)或拒绝(rejected)为 undefined。

  • executor 的完成状态对 Promise 的状态影响有限:

    executor 函数的返回值会被忽略。executor 函数中的 return 语句仅影响控制流程,调整函数某个部分是否执行,但不会影响 Promise 的兑现值。如果 executor 函数退出,且未来不可能调用 resolveFunc 或 rejectFunc(例如,没有安排异步任务),那么 Promise 将永远保持待定状态。

    如果在 executor 函数中抛出错误,则 Promise 将被拒绝,除非 resolveFunc 或 rejectFunc 已经被调用。

常规的 Promise 工作流程:

  1. 在构造函数生成新的 Promise 对象时,它还会生成一对相应的 resolveFunc 和 rejectFunc 函数;它们与 Promise 对象“绑定”在一起。
  2. executor 通常会封装某些提供基于回调的 API 的异步操作。回调函数(传给原始回调 API 的函数)在 executor 代码中定义,因此它可以访问 resolveFunc 和 rejectFunc。
  3. executor 是同步调用的(在构造 Promise 时立即调用),并将 resolveFunc 和 rejectFunc 函数作为传入参数。
  4. executor 中的代码有机会执行某些操作。异步任务的最终完成通过 resolveFunc 或 rejectFunc 引起的副作用与 Promise 实例进行通信。这个副作用让 Promise 对象变为“已解决”状态。
    • 如果先调用 resolveFunc,则传入的值将解决。Promise 可能会保持待定状态(如果传入了另一个 thenable 对象),变为已兑现状态(在传入非 thenable 值的大多数情况下),或者变为已拒绝状态(在解析值无效的情况下)。

    • 如果先调用 rejectFunc,则 Promise 立即变为已拒绝状态。

    • 一旦 resolveFunc 或 rejectFunc 中的一个被调用,Promise 将保持解决状态。只有第一次调用 resolveFunc 或 rejectFunc 会影响 Promise 的最终状态,随后对任一函数的调用都不能更改兑现值或拒绝原因,也不能将其最终状态从“已兑现”转换为“已拒绝”或相反。

    • 如果 executor 抛出错误,则 Promise 被拒绝。但是,如果 resolveFunc 或 rejectFunc 中的一个已经被调用(因此 Promise 已经被解决),则忽略该错误。

    • 解决 Promise 不一定会导致 Promise 变为已兑现或已拒绝(即已敲定)。Promise 可能仍处于待定状态,因为它可能是用另一个 thenable 对象解决的,但它的最终状态将与已解决的 thenable 对象一致。

  5. 一旦 Promise 敲定,它会(异步地)调用任何通过 then()、catch() 或 finally() 关联的进一步处理程序。最终的兑现值或拒绝原因在调用时作为输入参数传给兑现和拒绝处理程序(请参阅 promise 的链式调用)。

示例

javascript
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();
  });
}
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();
  });
}

Promise.resolve()

  • 描述: 可用于将现有对象转为 Promise 对象。
javascript
Promise.resolve(value)
Promise.resolve(value)
  • 返回value 参数的四种情况:
    1. 如果参数是 Promise 实例,Promise.resolve将不做任何修改、原封不动地返回这个实例。
    2. 参数是一个thenable对象,会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then方法。
    3. 不具有then方法的对象,或不是对象,返回一个新的 Promise 对象,状态为fulfilled 。
    4. 不带有任何参数,直接返回一个fulfilled 状态的 Promise 对象。
javascript
const promise = Promise.resolve(123);
promise.then((res) => {
  console.log(res); // 123
});
const promise = Promise.resolve(123);
promise.then((res) => {
  console.log(res); // 123
});

Promise.reject()

  • 描述:返回一个已拒绝(rejected)的 Promise 对象,拒绝原因为给定的参数。
javascript
let promise = Promise.reject('error'); // Uncaught (in promise) error
promise.catch((err) => {
  console.log(err); // error
})
let promise = Promise.reject('error'); // Uncaught (in promise) error
promise.catch((err) => {
  console.log(err); // error
})

Promise.all()

javascript
Promise.all(iterable)
Promise.all(iterable)
  • 描述:可接受一个Promise数组(本文档已数组为例) 或 可迭代对象作为输入,并返回一个新 Promise。新 Promise 的状态由输入的成员解决。

    其实就是将多个 Promise 实例,包装成一个新的 Promise 实例。

  • 返回:返回一个 新的 Promise对象。

    ①:当 iterable 中所有输入的 Promise 都被兑现时,返回的 Promise 将被兑现,并返回一个包含所有兑现值的数组。顺序与传入时一致,而非按照兑现的时间顺序排列。(fulfilled)

    ②:当 iterable 中的任何一个 Promise 被拒绝,返回的 Promise 将被拒绝,并带有第一个被拒绝的原因。(rejected)

    ③:当 iterable 中为非空,且非 Promise待定状态,返回输入的数组 (fulfilled)

    ④:当 iterable 中为空,返回一个空数组[ ] (already fulfilled)

示例

等待所有的传入成员兑现完成,再兑现promiseAll

javascript
const promise1 = new Promise((resolve, reject) => {
    setTimeout(() => resolve('one'), 1000);
})
const promise2 = new Promise((resolve, reject) => {
    setTimeout(() => resolve('two'), 2000);
})
const promise3 = new Promise((resolve, reject) => {
    setTimeout(() => resolve('three'), 1500);
})
const promiseAll = Promise.all([promise1, promise2,promise3, promise3]);
promiseAll.then((res) => {
    console.log(res) //fulfilled 等待所有成员兑现 2s 后 打印 ['one', 'two', 'three']
}).catch((err) =>{
    console.log(err)
})
const promise1 = new Promise((resolve, reject) => {
    setTimeout(() => resolve('one'), 1000);
})
const promise2 = new Promise((resolve, reject) => {
    setTimeout(() => resolve('two'), 2000);
})
const promise3 = new Promise((resolve, reject) => {
    setTimeout(() => resolve('three'), 1500);
})
const promiseAll = Promise.all([promise1, promise2,promise3, promise3]);
promiseAll.then((res) => {
    console.log(res) //fulfilled 等待所有成员兑现 2s 后 打印 ['one', 'two', 'three']
}).catch((err) =>{
    console.log(err)
})

传入成员任何一个兑现失败,则返回的 Promise 将被拒绝

javascript
const promise1 = new Promise((resolve, reject) => {
    setTimeout(() => resolve('one'), 1000);
})
const promise2 = new Promise((resolve, reject) => {
    setTimeout(() => resolve('two'), 2000);
})
const promise3 = new Promise((resolve, reject) => {
    setTimeout(() => reject(new Error('完蛋了')), 1500); // 兑现失败
})
const promiseAll = Promise.all([promise1, promise2,promise3, promise3]);
promiseAll.then((res) => {
    console.log(res)
}).catch((err) =>{
    console.log(err) // rejected  1.5 s 后 抛出异常 Error: 完蛋了
})
const promise1 = new Promise((resolve, reject) => {
    setTimeout(() => resolve('one'), 1000);
})
const promise2 = new Promise((resolve, reject) => {
    setTimeout(() => resolve('two'), 2000);
})
const promise3 = new Promise((resolve, reject) => {
    setTimeout(() => reject(new Error('完蛋了')), 1500); // 兑现失败
})
const promiseAll = Promise.all([promise1, promise2,promise3, promise3]);
promiseAll.then((res) => {
    console.log(res)
}).catch((err) =>{
    console.log(err) // rejected  1.5 s 后 抛出异常 Error: 完蛋了
})

传入成员为空或非Promised待定状态

javascript
console.log(Promise.all([])) // fulfilled
console.log(Promise.all([1])) // fulfilled [1]
console.log(Promise.all([])) // fulfilled
console.log(Promise.all([1])) // fulfilled [1]

Promise.any()

javascript
Promise.any(iterable)
Promise.any(iterable)
  • 描述:同样是将多个 Promise 实例,包装成一个新的 Promise 实例。只要传入成员被兑现为 fulfilled 状态,返回的Promise就为 fulfilled 状态。

  • 返回:如果被兑现,新返回的 Promise 只返回一个值。

    ①、当 iterable 中的任何一个 Promise 被兑现时,返回的 Promise 就会被兑现。其兑现值是第一个兑现的 Promise 的兑现值(fulfilled)

    ②、当 iterable 中的所有 Promise 都被拒绝时,返回的 Promise 被拒绝,并且抛出异常(rejected)

    ③、当 iterable 中为非空,且非 Promise待定状态,则返回的 Promise 仍然是异步拒绝的rejected--MDN (代码测试为兑现的状态fulfilled与其描述不符)(fulfilled)

    ④、当 iterable 中为空,将被拒绝并且抛出异常, (already rejected)

javascript
// 只要一个被兑现,就返回一个状态fulfilled为的Promise,和兑现的值
const promise1 = new Promise((resolve, reject) => {
  setTimeout(reject, 500, 'one');
});
const promise2 = new Promise((resolve, reject) => {
  setTimeout(resolve, 1000, 'two');
});
Promise.any([promise1, promise2]).then((res) => {
  console.log(res); // 1s 后打印 two 
}).catch((err) => {
  console.error(err);
});

// 传入为空,返回rejected状态
Promise.any([]).then((res) => {
  console.log(res);
}).catch((err) => {
  console.error(err); // 抛出异常
});

// 传入值为非空且非promise待确状态,返回fulfilled状态
Promise.any([1, 2, 3]).then((res) => {
    console.log(res); // 只返回传入时排第一个的值 1
}).catch((err) => {
    console.error(err); 
});
// 只要一个被兑现,就返回一个状态fulfilled为的Promise,和兑现的值
const promise1 = new Promise((resolve, reject) => {
  setTimeout(reject, 500, 'one');
});
const promise2 = new Promise((resolve, reject) => {
  setTimeout(resolve, 1000, 'two');
});
Promise.any([promise1, promise2]).then((res) => {
  console.log(res); // 1s 后打印 two 
}).catch((err) => {
  console.error(err);
});

// 传入为空,返回rejected状态
Promise.any([]).then((res) => {
  console.log(res);
}).catch((err) => {
  console.error(err); // 抛出异常
});

// 传入值为非空且非promise待确状态,返回fulfilled状态
Promise.any([1, 2, 3]).then((res) => {
    console.log(res); // 只返回传入时排第一个的值 1
}).catch((err) => {
    console.error(err); 
});

Promise.race()

javascript
Promise.race(iterable)
Promise.race(iterable)
  • 描述:同样是将多个 Promise 实例,包装成一个新的 Promise 实例。但返回的是第一个状态被改变的成员,不管兑现成功失败。

  • 返回

    ①、当 iterable 中第一个状态被被改变的成员决定:兑现(fulfilled),则返回该成员兑现结果,拒绝(rejected),抛出异常。

    ②、当 iterable 中为非空,且非 promise待定状态,返回新的promise将被兑现,值为传入时的第一个值(fulfilled)

    ③、当 iterable 中为空,返回的 promise 就会一直保持待定状态,它的then方法将不会执行 (pending)

javascript
const promise1 = new Promise((resolve, reject) => {
    setTimeout(() => resolve('one'), 1000);
})
const promise2 = new Promise((resolve, reject) => {
    setTimeout(() => resolve('two'), 2000);
})
const promise3 = new Promise((resolve, reject) => {
    setTimeout(() => reject(new Error('完蛋了')), 1500);
})
const promiseRace = Promise.race([promise1, promise2, promise3]);
promiseRace.then((res) => {
    console.log(res) // 返回第一个被兑现的 1s 后打印 one
}).catch((err) => {
    console.log(err)
})

Promise.race([]) // Promise {<pending>}  永远也无法被兑现 它的.then 不会执行
Promise.race([1]) // Promise {<fulfilled>}  1
const promise1 = new Promise((resolve, reject) => {
    setTimeout(() => resolve('one'), 1000);
})
const promise2 = new Promise((resolve, reject) => {
    setTimeout(() => resolve('two'), 2000);
})
const promise3 = new Promise((resolve, reject) => {
    setTimeout(() => reject(new Error('完蛋了')), 1500);
})
const promiseRace = Promise.race([promise1, promise2, promise3]);
promiseRace.then((res) => {
    console.log(res) // 返回第一个被兑现的 1s 后打印 one
}).catch((err) => {
    console.log(err)
})

Promise.race([]) // Promise {<pending>}  永远也无法被兑现 它的.then 不会执行
Promise.race([1]) // Promise {<fulfilled>}  1

Promise.allSettled()

javascript
Promise.allSettled(iterable)
Promise.allSettled(iterable)
  • 描述:接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。只有等到所有这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束。该方法由 ES2020 引入。

  • 返回

    ①、当 iterable 中所有 promise状态进行确定,要么兑现(fulfilled),要么拒绝(rejected)。兑现的值是数组对象(数组对象包含了兑现/拒绝的status、value/reason属性),顺序按照传入时的顺序,不受兑现快慢影响。

    ②、当 iterable 中为非空,且非 promise待定状态,则返回的 promise 的兑现数组,值为传入时的值(fulfilled)

    ②、当 iterable 中为空,返回一个空数组[ ](already fulfilled)

javascript
Promise.allSettled([]).then((res)=>{
  console.log(res); // []
})
Promise.allSettled([
  Promise.resolve(33),
  new Promise((resolve) => setTimeout(() => resolve(66), 0)),
  99,
  Promise.reject(new Error("一个错误")),
]).then((values) => console.log(values));
// [
//   { status: 'fulfilled', value: 33 },
//   { status: 'fulfilled', value: 66 },
//   { status: 'fulfilled', value: 99 },
//   { status: 'rejected', reason: Error: 一个错误 }
// ]
Promise.allSettled([]).then((res)=>{
  console.log(res); // []
})
Promise.allSettled([
  Promise.resolve(33),
  new Promise((resolve) => setTimeout(() => resolve(66), 0)),
  99,
  Promise.reject(new Error("一个错误")),
]).then((values) => console.log(values));
// [
//   { status: 'fulfilled', value: 33 },
//   { status: 'fulfilled', value: 66 },
//   { status: 'fulfilled', value: 99 },
//   { status: 'rejected', reason: Error: 一个错误 }
// ]

Promise.prototype.then()

javascript
then(onFulfilled, onRejected)
then(onFulfilled, onRejected)
  • 描述:方法最多接受两个参数:用于 Promise 兑现/拒绝情况的回调函数。它立即返回一个等效的 Promise 对象,允许你链接到其他 Promise 方法,从而实现链式调用

    1、它是 Promise 的基本方法:thenable 协议要求所有类 Promise 对象都提供一个 then() 方法,并且 catch() 和 finally() 方法都会通过调用该对象的 then() 方法来工作。

    2、如果在同一 Promise 对象上两次调用 then() 方法,每次调用 then() 方法返回的两个 Promise 对象开始了独立的链,不会等待彼此的敲定。

    3、沿着 then() 链产生的 thenable 对象总是会被解决——onFulfilled 处理函数永远不会收到 thenable 对象,并且任何由处理函数返回的 thenable 对象在传递给下一个处理函数之前总是被解决。这是因为在构造新的 Promise 对象时,由 executor 传递的 resolve 和 reject 函数被保存下来,在当前 Promise 对象敲定时,相应的函数将被调用,并传递兑现值或拒绝原因。解决逻辑来自 Promise() 构造函数传递的解决函数。

    4、then() 方法支持子类化,这意味着它可以在 Promise 子类的实例上调用,并且结果将是子类类型的 Promise 对象。你可以通过 [Symbol.species] 属性自定义返回值类型。

  • 参数:onFulfilled, onRejected 都是可选参数。

    onFulfilled:一个在此 Promise 对象被兑现时异步执行的函数。该函数的参数为(value),Promise 对象的兑现值。

    onRejected:一个在此 Promise 对象被拒绝时异步执行的函数。该函数的参数为(reason),Promise 对象被拒绝的原因。

Promise.prototype.catch()

  • 描述:用于注册一个在 promise 被拒绝时调用的函数。它会立即返回一个等效的 Promise 对象,这可以允许你链式调用其他 promise 的方法。

    是 Promise.prototype.then(undefined, onRejected) 的一种简写形式。

Promise.prototype.finally()

  • 描述:用于注册一个在 promise 敲定(兑现或拒绝)时调用的函数。它会立即返回一个等效的 Promise 对象,这可以允许你链式调用其他 promise 方法。

示例

javascript
let isLoading = true;
fetch(myRequest)
  .then((response) => {
    const contentType = response.headers.get("content-type");
    if (contentType && contentType.includes("application/json")) {
      return response.json();
    }
    throw new TypeError("Oops, we haven't got JSON!");
  })
  .then((json) => {
    /* 进一步处理 JSON */
  })
  .catch((error) => {
    console.error(error); // 这行代码也可能抛出错误,例如:when console = {}
  })
  .finally(() => {
    isLoading = false;
  });
let isLoading = true;
fetch(myRequest)
  .then((response) => {
    const contentType = response.headers.get("content-type");
    if (contentType && contentType.includes("application/json")) {
      return response.json();
    }
    throw new TypeError("Oops, we haven't got JSON!");
  })
  .then((json) => {
    /* 进一步处理 JSON */
  })
  .catch((error) => {
    console.error(error); // 这行代码也可能抛出错误,例如:when console = {}
  })
  .finally(() => {
    isLoading = false;
  });