一、在Promise类中,

  • 有5类静态方法:Promise.resolve()、Promise.reject()、Promise.all()、Promise.allSettled()、Promise.race()。
  • 2类动态方法:Promise.any()、Promise.try()

    Promise方法

一、在现代的代码中,很少需要使用Promise.resolve和Promise.reject方法,因为async/await语法使它们变得有些过时了。

Promise.all()

一、等待所有 promise 都 resolve 时,返回存放它们结果的数组。如果给定的任意一个 promise 为 reject,那么它就会变成Promise.all的 error,所有其他 promise 的结果都会被忽略。
二、Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例

  1. const p = Promise.all([p1, p2, p3]);

1、Promise.all()方法接受一个数组作为参数,p1p2p3都是 Promise 实例,如果不是,就会调用Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。
2、Promise.all()方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。
3、p的状态由p1p2p3决定,分成两种情况。
(1)只有p1p2p3的状态都变成fulfilledp的状态才会变成fulfilled,此时p1p2p3的返回值组成一个数组,传递给p的回调函数。
(2)只要p1p2p3之中有一个被rejectedp的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
三、示例
【示例1】

  1. let promise = Promise.all([1, 2, 3]);
  2. promise.then(value => {
  3. console.log(value); // [1,2,3]
  4. });
  5. console.log(promise);

1、promise结果:
【见】Promise API - 图1
【示例2】

  1. let p2 = Promise.reject(2);
  2. let promise = Promise.all([1, p2, 3]);
  3. promise
  4. .then(value => {
  5. console.log(value);
  6. })
  7. .catch(err => {
  8. console.log(err); // 2
  9. });
  10. console.log(promise);

1、promise结果:
【见】Promise API - 图2
【示例3】 Promise.all 在 3 秒之后被 settled,然后它的结果就是一个 [1, 2, 3] 数组

Promise.all([
  new Promise(resolve => setTimeout(() => resolve(1), 3000)), // 1
  new Promise(resolve => setTimeout(() => resolve(2), 2000)), // 2
  new Promise(resolve => setTimeout(() => resolve(3), 1000))  // 3
]).then(alert); // 1,2,3 当上面这些 promise 准备好时:每个 promise 都贡献了数组中的一个元素

1、请注意,结果数组中元素的顺序与其在源 promise 中的顺序相同。即使第一个 promise 花费了最长的时间才 resolve,但它仍是结果数组中的第一个。
【示例4】常见技巧:将一个任务数据数组映射(map)到一个 promise 数组,然后将其包装到 Promise.all。如:如果我们有一个存储 URL 的数组,我们可以像这样 fetch 它们:

let urls = [
  'https://api.github.com/users/iliakan',
  'https://api.github.com/users/remy',
  'https://api.github.com/users/jeresig'
];

// 将每个 url 映射(map)到 fetch 的 promise 中
let requests = urls.map(url => fetch(url));

// Promise.all 等待所有任务都 resolved
Promise.all(requests)
  .then(responses => responses.forEach(
    response => alert(`${response.url}: ${response.status}`)
  ));

【示例5】通过 GitHub 用户名来获取一个 GitHub 用户数组中用户的信息(我们也可以通过商品 id 来获取商品数组中的商品信息,逻辑都是一样的)

let names = ['iliakan', 'remy', 'jeresig'];

let requests = names.map(name => fetch(`https://api.github.com/users/${name}`));

Promise.all(requests)
  .then(responses => {
    // 所有响应都被成功 resolved
    for(let response of responses) {
      alert(`${response.url}: ${response.status}`); // 对应每个 url 都显示 200
    }

    return responses;
  })
  // 将响应数组映射(map)到 response.json() 数组中以读取它们的内容
  .then(responses => Promise.all(responses.map(r => r.json())))
  // 所有 JSON 结果都被解析:"users" 是它们的数组
  .then(users => users.forEach(user => alert(user.name)));

【示例6】如果任意一个 promise 被 reject,由 Promise.all 返回的 promise 就会立即 reject,并且带有的就是这个 error。

Promise.all([
  new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),
  new Promise((resolve, reject) => setTimeout(() => reject(new Error("Whoops!")), 2000)),
  new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000))
]).catch(alert); // Error: Whoops!

三、如果作为参数的 Promise 实例,自己定义了catch方法,那么它一旦被rejected,并不会触发Promise.all()catch方法

const p1 = new Promise(resolve => {
  resolve("hello");
})
.then(result => result)
.catch(e => e);
const p2 = new Promise(() => {
  throw new Error("报错了");
})
.then(result => result)
.catch(e => e); // p2实际上是catch返回的promise实例
Promise.all([p1, p2])
  .then(result => console.log(result))
  .catch(e => console.log(e));

三、Promise.all(iterable)允许在iterable中使用 non-promise 的“常规”值
1、Promise.all(…)接受可迭代对象(iterable)的 promise(大多数情况下是数组)。但是,如果这些对象中的任意一个都不是 promise,那么它将被“按原样”传递给结果数组。
【示例1】这里的结果是[1, 2, 3]:

Promise.all([
  new Promise((resolve, reject) => {
    setTimeout(() => resolve(1), 1000)
  }),
  2,
  3
]).then(alert); // 1, 2, 3

所以我们可以在方便的地方将准备好的值传递给Promise.all。

Promise.allSettled() :ES2020 新增方法

一、Promise.allSettled()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。
1、只有等到所有这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束。
二、参数与Promise.all()方法一样

let p2 = Promise.reject(2);
let promise = Promise.allSettled([1, p2, 3]);
promise.then(value => {
  console.log(value); // [{status: "fulfilled", value: 1},{status: "rejected", reason: 2},{status: "fulfilled", value: 3}]
});
console.log(promise);

三、Promise.allSettled()的返回的promise实例状态只可能变成resolved,它的监听函数收到的参数是一个数组,该数组的每个成员都是一个对象,每一个对象都有status属性,该属性的值只可能是字符串fulfilled或字符串rejected
1、fulfilled时,对象有value属性,rejected时有reason属性,对应两种状态的返回值。
2、结果数组:
(1){status:”fulfilled”, value:result} 对于成功的响应,
(2){status:”rejected”, reason:error} 对于 error。
四、示例
【示例1】我们想要获取(fetch)多个用户的信息。即使其中一个请求失败,我们仍然对其他的感兴趣

let urls = [
  'https://api.github.com/users/iliakan',
  'https://api.github.com/users/remy',
  'https://no-such-url'
];

Promise.allSettled(urls.map(url => fetch(url)))
  .then(results => { // (*)
    results.forEach((result, num) => {
      if (result.status == "fulfilled") {
        alert(`${urls[num]}: ${result.value.status}`);
      }
      if (result.status == "rejected") {
        alert(`${urls[num]}: ${result.reason}`);
      }
    });
  });

1、上面的 (*) 行中的 results 将会是

[
  {status: 'fulfilled', value: ...response...},
  {status: 'fulfilled', value: ...response...},
  {status: 'rejected', reason: ...error object...}
]

2、对于每个 promise,我们都得到了其状态(status)和 value/reason

Polyfill

如果浏览器不支持Promise.allSettled,很容易进行 polyfill:
if (!Promise.allSettled) {
const rejectHandler = reason => ({ status: ‘rejected’, reason });

const resolveHandler = value => ({ status: ‘fulfilled’, value });

Promise.allSettled = function (promises) {
const convertedPromises = promises.map(p => Promise.resolve(p).then(resolveHandler, rejectHandler));
return Promise.all(convertedPromises);
};
}
在这段代码中,promises.map获取输入值,并通过p => Promise.resolve(p)将输入值转换为 promise(以防传递了 non-promise),然后向每一个 promise 都添加.then处理程序(handler)。
这个处理程序(handler)将成功的结果value转换为{status:’fulfilled’, value},将 errorreason转换为{status:’rejected’, reason}。这正是Promise.allSettled的格式。
然后我们就可以使用Promise.allSettled来获取所有给定的 promise 的结果,即使其中一些被 reject。

Promise.any()

该方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例返回。只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态。Promise.any()Promise.race()方法很像,只有一点不同,就是不会因为某个 Promise 变成rejected状态而结束。

let p1 = Promise.reject(1);
let p2 = Promise.reject(2);
let promise = Promise.any([p1, p2, 3]);
promise.then(value => {
  console.log(value); // 3
});
console.log(promise);

当所有的实例返回的状态都是rejected时,Promise.any()会返回一个的实例状态才为rejected

let p1 = Promise.reject(1);
let p2 = Promise.reject(2);
let promise = Promise.any([p1, p2]);
promise
  .then(value => {
  console.log(value);
})
  .catch(value => {
  console.log(value); // AggregateError: All promises were rejected
});
console.log(promise);

Promise.race()

一、等待第一个 settle 的 promise,并将其 result/error 作为结果。
二、Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。Promise.race()方法的参数与Promise.all()方法一样,如果不是 Promise 实例,就会先调用下面讲到的Promise.resolve()方法,将参数转为 Promise 实例,再进一步处理。

let promise = Promise.race(iterable);

【示例1】

const p = Promise.race([p1, p2, p3]);

1、上面代码中,只要p1p2p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。
三、与Promise.all类似,但只等待第一个 settled 的 promise 并获取其结果(或 error)。
【示例1】这里的结果将是1:

Promise.race([
  new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),
  new Promise((resolve, reject) => setTimeout(() => reject(new Error("Whoops!")), 2000)),
  new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000))
]).then(alert); // 1

1、这里第一个 promise 最快,所以它变成了结果。第一个 settled 的 promise “赢得了比赛”之后,所有进一步的 result/error 都会被忽略。

Promise.reject()

一、Promise.reject(error)用error创建一个 rejected 的 promise
二、Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected

const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))

Promise.resolve()

一、使用给定 value 创建一个 resolved 的 promise
二、有时需要将现有对象转为 Promise 对象,Promise.resolve()方法就起到这个作用,且实例状态为resolve

Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))

Promise.resolve()方法的参数

Promise.resolve()方法的参数分成四种情况

参数是一个 Promise 实例

const promise = new Promise(resolve => {
  resolve("resolve");
});
let p = Promise.resolve(promise);
console.log(p == promise); // true

参数是一个thenable对象

一、thenable对象指的是具有then方法的对象,Promise.resolve()方法会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then()方法

// thenable对象
let thenable = {
  then: function(resolve, reject) {
    resolve(42);
  }
};
let p1 = Promise.resolve(thenable);
p1.then(function (value) {
  console.log(value);  // 42
});

上面代码中,thenable对象的then()方法执行后,对象p1的状态就变为resolved,从而立即执行最后那个then()方法指定的回调函数,输出42

参数不是具有then()方法的对象,或根本就不是对象

【示例1】

const p = Promise.resolve('Hello');
p.then(function (s) {
  console.log(s) // Hello
});

【示例2】下面的loadCached函数获取(fetch)一个 URL 并记住其内容。以便将来对使用相同 URL 的调用,它能立即从缓存中获取先前的内容,但使用Promise.resolve创建了一个该内容的 promise,所以返回的值始终是一个 promise。

let cache = new Map();

function loadCached(url) {
  if (cache.has(url)) {
    return Promise.resolve(cache.get(url)); // (*)
  }

  return fetch(url)
    .then(response => response.text())
    .then(text => {
      cache.set(url,text);
      return text;
    });
}

1、我们可以使用loadCached(url).then(…),因为该函数保证了会返回一个 promise。我们就可以放心地在loadCached后面使用.then。这就是(*)行中Promise.resolve的目的。

不带有任何参数

一、Promise.resolve()方法允许调用时不带参数,直接返回一个resolved状态的 Promise 对象
Promise.resolve();

Promise.resolve();

// 相当于
new Promise(resolve => resolve(undefined))


Promise.try()(20220316-提案)

实际开发中,经常遇到一种情况:不知道或者不想区分,函数f是同步函数还是异步操作,但是想用 Promise 来处理它。因为这样就可以不管f是否包含异步操作,都用then方法指定下一步流程,用catch方法处理f抛出的错误。一般就会采用下面的写法。

const f = () => console.log('now');
Promise.resolve().then(f);
console.log('next');
// next
// now


上面的写法有一个缺点,就是如果f是同步函数,那么它会在本轮事件循环的末尾执行。鉴于这是一个很常见的需求,所以现在有一个提案,提供Promise.try方法替代上面的写法。

const f = () => console.log('now');
Promise.try(f);
console.log('next');
// now
// next

消费者:then,catch,finally

一、Promise 对象充当的是 executor(“生产者代码”)和消费函数)之间的连接,后者将接收结果或 error。可以通过使用.then、.catch和.finally方法为消费函数进行注册。

catch()

一、如果我们只对 error 感兴趣,那么我们可以使用null作为第一个参数:.then(null, errorHandlingFunction)。或者我们也可以使用.catch(errorHandlingFunction),其实是一样的:

let promise = new Promise((resolve, reject) => {
  setTimeout(() => reject(new Error("Whoops!")), 1000);
});

// .catch(f) 与 promise.then(null, f) 一样
promise.catch(alert); // 1 秒后显示 "Error: Whoops!"

1、.catch(f)调用是.then(null, f)的完全的模拟,它只是一个简写形式。

Promise.prototype.catch()

一、catch()方法是.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数。

const promise = new Promise((_, reject) => {
  reject("reject");
});
promise
  .then(value => {
  console.log(value);
})
 // 发生错误,或者reject时执行
  .catch(value => {
  console.log(value);
});

二、如果 Promise 状态已经变成resolved,再抛出错误是无效的。

const promise = new Promise(resolve => {
  resolve("resolve");
  throw new Error("fail");
});
promise.then(value => console.log(value));

三、promise中所有没有被处理的错误都会冒泡到最后一个catch中

const promise = new Promise(resolve => {
  resolve("resolve");
});
promise
  .then(value => {
  console.log(value);
  throw new Error("fail1");
})
  .then(() => {
  throw new Error("fail2");
})
  .catch(value => {
  console.log(value);
});

在上面的代码中,catch会优先打印打印第一个错误,当第一个错误解决之后(注释掉就ok),catch里才会打印第二个错误 catch的返回值仍是promise,返回promise的方式和then相似,因此,catch后仍然可以调用then方法

finally()

一、就像常规try {…} catch {…}中的finally子句一样,promise 中也有finally。
二、.finally(f)调用与.then(f, f)类似,在某种意义上,f总是在 promise 被 settled 时运行:即 promise 被 resolve 或 reject。
三、finally是执行清理(cleanup)的很好的处理程序(handler),例如无论结果如何,都停止使用不再需要的加载指示符(indicator)。
像这样:

new Promise((resolve, reject) => {
  /* 做一些需要时间的事儿,然后调用 resolve/reject */
})
  // 在 promise 为 settled 时运行,无论成功与否
  .finally(() => stop loading indicator)
  // 所以,加载指示器(loading indicator)始终会在我们处理结果/错误之前停止
  .then(result => show result, err => show error)

四、finally(f)其实并不是then(f,f)的别名。它们之间有一些细微的区别:

  1. finally处理程序(handler)没有参数。在finally中,我们不知道 promise 是否成功。没关系,因为我们的任务通常是执行“常规”的定稿程序(finalizing procedures)。
  2. finally处理程序将结果和 error 传递给下一个处理程序。例如,在这儿结果被从finally传递给了then:
    new Promise((resolve, reject) => {
    setTimeout(() => resolve("result"), 2000)
    })
    .finally(() => alert("Promise ready"))
    .then(result => alert(result)); // <-- .then 对结果进行处理
    
    在这儿,promise 中有一个 error,这个 error 被从finally传递给了catch:
    new Promise((resolve, reject) => {
    throw new Error("error");
    })
    .finally(() => alert("Promise ready"))
    .catch(err => alert(err));  // <-- .catch 对 error 对象进行处理
    
    这非常方便,因为finally并不是意味着要处理 promise 的结果。所以它将结果传递了下去。
    五、我们可以对 settled 的 promise 附加处理程序
    1、如果 promise 为 pending 状态,.then/catch/finally处理程序(handler)将等待它。否则,如果 promise 已经是 settled 状态,它们就会运行: ```javascript // 下面这 promise 在被创建后立即变为 resolved 状态 let promise = new Promise(resolve => resolve(“done!”));

promise.then(alert); // done!(现在显示)

2、我们可以随时添加处理程序(handler):如果结果已经在了,它们就会执行。
<a name="QGFtf"></a>
#### Promise.prototype.finally()
一、finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。finally方法的回调函数不接受任何参数,这表明,finally方法里面的操作,应该是与状态无关的,不依赖于 Promise 的执行结果。
```javascript
const promise = new Promise(resolve => {
  resolve("resolve");
});
promise.finally(() => {
  console.log(11); // 11
});

二、finally的本质

promise.finally(() => {
  // do something
});
// 等同于
promise.then(
  result => {
    // do something
    return result;
  },
  error => {
    // do something
    throw error;
  }
);

1、finally的返回值为一个新的和原来的值相似的promise

then()

一、语法如下:

promise.then(
  function(result) { /* handle a successful result */ },
  function(error) { /* handle an error */ }
);

1、.then的第一个参数是一个函数,该函数将在 promise resolved 后运行并接收结果。
2、.then的第二个参数也是一个函数,该函数将在 promise rejected 后运行并接收 error。
二、【示例1】以下是对成功 resolved 的 promise 做出的反应:

let promise = new Promise(function(resolve, reject) {
  setTimeout(() => resolve("done!"), 1000);
});

// resolve 运行 .then 中的第一个函数
promise.then(
  result => alert(result), // 1 秒后显示 "done!"
  error => alert(error) // 不运行
);

1、第一个函数被运行了。
2、在 reject 的情况下,运行第二个:

let promise = new Promise(function(resolve, reject) {
  setTimeout(() => reject(new Error("Whoops!")), 1000);
});

// reject 运行 .then 中的第二个函数
promise.then(
  result => alert(result), // 不运行
  error => alert(error) // 1 秒后显示 "Error: Whoops!"
);

3、如果我们只对成功完成的情况感兴趣,那么我们可以只为.then提供一个函数参数:

let promise = new Promise(resolve => {
  setTimeout(() => resolve("done!"), 1000);
});

promise.then(alert); // 1 秒后显示 "done!"

Promise.prototype.then()

一、then方法是定义在原型对象Promise.prototype上的,同时then方法的两个参数都是非必选的。因为then方法返回的是一个全新的promise实例时,因此then方法可以链式调用 then方法在以下情况返回的promise
1、当未传入参数时,then方法会返回一个新的,状态和原promise相同的promise

const promise = new Promise(resolve => {
  resolve("resolve");
});
let p = promise.then();
console.log(promise);
console.log(p);

结果展示
【见】Promise API - 图3
2、上一个promise未被成功调用then方法时,返回的结果如情形1

const promise = new Promise((_, reject) => {
  reject("reject");
});
let a = promise.then(value => {
  console.log(value);
});

3、上一个promise被成功调用then方法时,返回一个resolve(undefined)的promise

const promise = new Promise((_, reject) => {
  reject("reject");
});
let a = promise.then(undefined, value => {
  console.log(value);
});
console.log(a);

4、上一个promise被成功调用then方法,但出现报错,返回一个reject('error')的promise,error代指错误,并非真正的reject返回的结果

const promise = new Promise(resolve => {
  resolve("resolve");
});
let p = promise.then(value => {
  console.log(value);
  throw new Error("fail1");
});
console.log(p);

结果展示:
【见】Promise API - 图4
5、给then方法手动返回一个promise,此时会覆盖掉默认的行为,返回值为新增的promise

const promise = new Promise(resolve => {
  resolve("resolve");
});
let p = promise.then(
  () =>
  new Promise(resolve => {
    resolve("resolve2");
  })
);
console.log(p);

一、对promise.then的调用会返回了一个 promise
一、then里的参数是可选的,catch(failureCallback)是then(null, failureCallback)的缩略形式。

| 【示例】```javascript doSomething().then(function(result) { return doSomethingElse(result); }) .then(function(newResult) { return doThirdThing(newResult); }) .then(function(finalResult) { console.log(‘Got the final result: ‘ + finalResult); }) .catch(failureCallback);

1、一定要有返回值,否则,callback将无法获取上一个Promise的结果。<br />2、也可以用箭头函数来表示<br />(1)如果使用箭头函数,() => x 比 () => { return x }更简洁一些,但后一种保留return的写法才支持使用多个语句。```javascript
doSomething()
.then(result => doSomethingElse(result))
.then(newResult => doThirdThing(newResult))
.then(finalResult => {
  console.log(`Got the final result: ${finalResult}`);
})
.catch(failureCallback);


| | —- |

组合:Promise.all()、Promise.race()

一、Promise.resolve()和Promise.reject()是手动创建一个已经resolve或者reject的Promise快捷方法。
二、Promise.all()和Promise.rece()是并行运行异步操作的两个组合式工具。

| 【示例】发起并行操作,然后等多个操作全部结束后进行下一步操作```javascript Promise.all([func1(), func2(), func3()]) .then(([result1, result2, result3])) => { / use result1, result2 and result3 / })

 |
| --- |

| 【示例】可以使用一些聪明的JavaScript写法实现时序组合```javascript
[func1, func2, func3].reduce((p, f) => p.then(f), Promise.resolve())
    .then(result3 => { /* use result3 */ })

| | —- |

| 【示例】递归调用由异步函数组成的数组时,相当于一个Promise链```javascript Promise.resolve().then(func1).then(func2).then(func3)

 |
| --- |

| 【示例】可以写成可复用的函数形式,这在函数式编程中极为普遍```javascript
const applyAsync = (acc, val) => acc.then(val)
const composeAsync = (...funcs) => x => funcs.reduce(applyAsync, Promise.resolve(x))

1、composeAsync()函数将会接受任意数量的函数作为其参数,并返回一个新的函数,该函数接受一个通过composition pipeline传入的初始值。
(1)任一函数可以是异步或同步的,composeAsync()函数能保证它们按顺序执行。```javascript const transformData = composeAsync(func1, func2, func3) const result3 = transformData(data)

 |
| --- |

| 【示例】在ECMAScript2017标准中,时序组合可以通过使用async/await而变得更简单```javascript
let result
for (const f of [func1, func2, func3]) {
    result = await f(result)
}
/* use last result (i.e. result3) */

| | —- |

时序

一、为了避免意外,即使是一个已经变成resolve状态的Promise,传递给then()的函数也总是会被异步调用

| 【示例】```javascript Promise.resolve().then(() => console.log(2)) console.log(1)

// 1, 2

 |
| --- |

二、传递到then()中的函数被置入到一个微任务队列中,而不会立即执行,这意味着它是在JavaScript事件队列的所有运行时结束了,且事件队列被清空之后,才开始执行

| 【示例】```javascript
const wait = ms => new Promise(resolve => setTimeout(resolve, ms))
wait().then(() => console.log(4))
Promise.resolve().then(() => console.log(2)).then(() => console.log(3))
console.log(1)

// 1, 2, 3, 4

| | —- |