/

如何得到一个Promise对象

  • new Promise()
  • 类方法 Promise.xxx(): .resolve() .reject() .all() .race()
  • 原型方法 Promise.prototype.xxx(): .then() .catch() .finally()

特性

  1. Promise 对象的错误具有“冒泡”特性,会一直向后传递,直到被捕获为止;这就是为什么 .catch() 和 .then()的第二个参数方法 的作用一致。
  2. Promise 内部的错误不会影响到 Promise 外部的代码,俗称“Promise 会吃掉错误”。所以建议使用 .catch 而不是 .then 的第二个参数来捕获 reject 行为。

基本用法

  1. const p = new Promise((resolve, reject) => {
  2. if(/*success*/) {
  3. resolve(/* success result */)
  4. } else {
  5. reject(/* fail reason */)
  6. }
  7. })
  8. /**
  9. * 以下为注释版
  10. */
  11. // p将是一个promise实例,在new时传入的resolve、reject是形参,从后文中取得
  12. // Promise将在声明时立即执行,没法被打断,所以当运行这行代码的时候,上文环境中就多一个叫p的promise实例,值是对应状态的实例
  13. const p = new Promise((resolve, reject) => {
  14. // 一般promise用于执行异步操作
  15. // 当异步操作成功时,执行从后文传入的resolve位置方法,并传值, 传值可以是任意类型
  16. // 如果后文没有传入resolve方法,不会报错,也不会有任何反应
  17. if(/*success*/) {
  18. resolve(/* success result */)
  19. } else {
  20. // 当异步操作失败时,执行从后文传入的reject位置方法,并传值,传值可以是任意类型
  21. // 如果后文没有传入reject方法,会报错 Uncaught(in promise)
  22. reject(/* fail reason */)
  23. }
  24. })

实例方法

then()

  • then 的第一个参数即为 resolve 状态下的回调函数,也即是上文注释中「从后文传入的resolve位置方法」;第二个参数是 reject状态下的回调函数
  • then 方法返回一个新的Promise实例(而不是原来的那个Promise实例!),因此 then 支持链式写法,可以 p.then().then()
  • 不同于第一个 then 的参数是从 promise 中拿取的,第二个及后面的参数来自于前一个 then 的 return
  • 如果前一个 then 中 return 的依旧是一个 promise,那么后一个 then 会等待这个promise执行完,如以下示例代码
  1. const p = new Promise((resolve, reject) => {
  2. resolve('1')
  3. }).then((res) => {
  4. console.log('第一个then');
  5. return new Promise(resolve => {
  6. setTimeout(() => {
  7. resolve('222')
  8. }, 1000)
  9. })
  10. }).then(res => {
  11. // 会间隔1s才打印
  12. console.log('第二个then');
  13. console.log(res);
  14. })

catch()

  • catch() 用于捕获之前的 Promise(不一定是上一个,因为错误会被冒泡),和 then 的第二个参数不同的是,他也可以捕获 resolve 回调参数中的运行时语法错误。
  • catch() 同样返回一个新的Promise实例,所以它也支持链式写法,并且
    如果之前的 Promise 没有错误,那么会跳过传入的 catch方法 ,直接执行后续的.then(),如下代码: ```javascript Promise.resolve() .catch(function(error) { console.log(‘oh no’, error); }) .then(function() { console.log(‘carry on’); });

// carry on

  1. <a name="2181dc73"></a>
  2. ### finally()
  3. - 不管 Promise 对象最后状态如何都会执行的操作
  4. ```javascript
  5. promise
  6. .then(result => {···})
  7. .catch(error => {···})
  8. .finally(() => {···});
  • finally 传入的回调方法不接受任何参数,这意味着没有办法知道 前面的 Promise 状态到底是fulfilled还是rejected。这表明,finally方法里面的操作,应该是与状态无关的,不依赖于 Promise 的执行结果。
  • finally 同样返回一个 Promise 实例,也即是可以继续向下 then 或者 catch,但是很奇怪的是, .finally().then(res) 的 then 获取不到finally return的值,如下代码:
  1. const p1 = new Promise(resolve => {
  2. setTimeout(() => { resolve('111') }, 1000)
  3. }).then(res => {
  4. console.log(res)
  5. }).finally(res => {
  6. console.log(res);
  7. return new Promise((resolve, reject) => {
  8. setTimeout(() => {
  9. resolve('ffffff')
  10. }, 1000)
  11. })
  12. }).then(res => {
  13. console.log(res); // undefined ???
  14. })

但是如果把resolve改为reject(‘ffffff’),catch(res) 是可以拿到值的,就很迷惑

类方法

all()

  • 用于将多个 Promise 实例,包装成一个新的 Promise 实例。
  1. const p = Promise.all([p1, p2, p3]);
  • 如果数组中的单项有不为 Promise 实例的,会调用 Promise.resolve() 将其转换为 Promise实例 再继续处理。
  • 如果数组中的每个 Promise 实例的状态都变为 fulfilled ,或者其中有一个变为 rejected ,p 的状态才会变化。
  • 如果结束时,数组中的每个 Promise 实例的状态都是 fulfilled ,那么拿到的值会是
  • 注意,当给数组中的Promise实例添加 .then 或者 .catch 的回调之后,all() 就无法获取到对应的回调了,这是因为这个变量已经指向 .then 或者 .catch 对应的Promise实例了,如以下示例:
  1. const p1 = new Promise(resolve => {
  2. setTimeout(() => {
  3. resolve('111')
  4. }, 1000)
  5. }).then(res => {
  6. console.log(res)
  7. return res + 'p1'
  8. })
  9. const p2 = new Promise(resolve => resolve('222')).then(res => {
  10. console.log(res)
  11. return res + 'p2'
  12. })
  13. Promise.all([p1, p2]).then(res => {
  14. console.log(res) // 输出会是 [ '111p1', '222p2' ]
  15. })

race()

  • 用于将多个 Promise 实例,包装成一个新的 Promise 实例。
  1. const p = Promise.race([p1, p2, p3]);
  • 如果数组中的单项有不为 Promise 实例的,会调用 Promise.resolve() 将其转换为 Promise实例 再继续处理。
  • 只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数

resolve()

  • 将现有对象转为 Promise 对象,返回一个 Promise实例,等价于:
  1. Promise.resolve('foo')
  2. // 等价于
  3. new Promise(resolve => resolve('foo'))
  • Promise.resolve() 接收四种参数:
    1. 一个 Promise 实例:原样返回。
    2. 一个具有 then 方法的对象:将对象转为 Promise对象,立即执行对象中的 then 方法。
    3. 不是具有then方法的对象,或根本就不是对象:返回一个新的 Promise 对象,状态为 resolved ,会将传入 Promise.resolve 这个值作为参数传给返回Promise 对象的回调函数。
    4. 不带有任何参数:直接返回一个resolved状态的 Promise 对象。

reject()

  • 除了 resolve 状态变成 reject 状态,和下面提到的参数区别,其余部分与 resolve 一样。
  • Promise.reject() 方法的参数,会原封不动地作为reject的理由,变成后续方法的参数。这一点与 Promise.resolve 方法不一致,resolve 是调用 then 中的方法。
  1. const thenable = {
  2. then(resolve, reject) {
  3. reject('出错了');
  4. }
  5. };
  6. Promise.reject(thenable).catch(e => {
  7. console.log(e === thenable)
  8. })
  9. // true

ES2020 : allSettled()

  • 用于将多个 Promise 实例,包装成一个新的 Promise 实例。
  1. const p = Promise.allSettled([p1, p2, p3]);
  • 只有等到所有这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束。该方法由 ES2020 引入。
  • 该方法返回的新的 Promise 实例,一旦结束,状态总是fulfilled,不会变成rejected。状态变成fulfilled后,Promise 的监听函数接收到的参数是一个数组,每个成员对应一个传入Promise.allSettled()的 Promise 实例。
  1. const resolved = Promise.resolve(42);
  2. const rejected = Promise.reject(-1);
  3. const allSettledPromise = Promise.allSettled([resolved, rejected]);
  4. allSettledPromise.then(function (results) {
  5. console.log(results);
  6. // [{status: 'fulfilled', value: 42}, {status: 'rejected', reason: -1}]
  7. });

手写