0%

promise基础用法

Promise 是异步编程的一种解决方案: 从语法上讲,promise是一个对象,从它可以获取异步操作的消息;从本意上讲,它是承诺,承诺它过一段时间会给你一个结果。 promise有三种状态:pending( 等待态 ),fulfiled( 成功态 ),rejected( 失败态 ) 状态一旦改变,就不会再变。创造promise实例后,它会立即执行。

promise是用来解决两个问题的:

  • 回调地狱,代码难以维护, 常常第一个的函数的输出是第二个函数的输入这种现象
  • promise可以支持多个并发的请求,获取并发请求中的数据
  • promise可以解决异步的问题,本身不能说promise是异步的

Promise.prototype.then()和Promise.prototype.catch()

resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去

reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。

一般来说,不要在then()方法里面定义 Reject 状态的回调函数(即then的第二个参数),总是使用catch方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 不建议这么使用
promise.then(function(data) {
// success
}, function(err) {
// error
});

// good
promise.then(function(data) { //cb
// success
}).catch(function(err) {
// error
});

第二种写法可以捕获前面then方法执行中的错误,也更接近同步的写法(try/catch)。

Promise.prototype.finally()

finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。

1
2
3
4
5
6
7
promise.then(result => {
...
}).catch(error => {
...
}).finally(() => {
...
});

上面代码中,不管promise最后的状态,在执行完thencatch指定的回调函数以后,都会执行finally方法指定的回调函数。
finally方法的回调函数不接受任何参数,应该是与状态无关的,不依赖于 Promise 的执行结果。
finally方法总是会返回原来的值。

Promise.all()

Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。

1
2
3
4
5
const p = Promise.all([p1, p2, p3]);
// p的状态由p1、p2、p3决定,分成两种情况。

// 只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
// 只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

Promise.all()方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。

Promise.race()

Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。

1
2
const p = Promise.race([p1, p2, p3]);
// 上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。

Promise.race()方法的参数与Promise.all()方法一样,如果不是 Promise 实例,就会先调用下面讲到的Promise.resolve()方法,将参数转为 Promise 实例,再进一步处理。

Promise.allSettled()

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

1
2
3
4
5
6
7
8
const promises = [
fetch('/api-1'),
fetch('/api-2'),
fetch('/api-3'),
];
await Promise.allSettled(promises);
removeLoadingIndicator();
//对服务器发出三个请求,等到三个请求都结束,不管请求成功还是失败,加载的滚动图标就会消失。

Promise.allSettled()的返回值allSettledPromise,状态只可能变成fulfilled。它的监听函数接收到的参数是数组results。该数组的每个成员都是一个对象,对应传入Promise.allSettled()的两个 Promise 实例。每个对象都有status属性,该属性的值只可能是字符串fulfilled或字符串rejectedfulfilled时,对象有value属性,rejected时有reason属性,对应两种状态的返回值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const resolved = Promise.resolve(42);
const rejected = Promise.reject(-1);
const allSettledPromise = Promise.allSettled([resolved, rejected]);

allSettledPromise.then(function (results) {
console.log(results);
});
// [
// { status: 'fulfilled', value: 42 },
// { status: 'rejected', reason: -1 }
// ]
//-------------------------------用法例子--------------------------------------
const promises = [ fetch('index.html'), fetch('https://does-not-exist/') ];
const results = await Promise.allSettled(promises);
// 过滤出成功的请求
const successfulPromises = results.filter(p => p.status === 'fulfilled');
// 过滤出失败的请求,并输出原因
const errors = results
.filter(p => p.status === 'rejected')
.map(p => p.reason);

有时候,我们不关心异步操作的结果,只关心这些操作有没有结束。这时,Promise.allSettled()方法就很有用。如果没有这个方法,想要确保所有操作都结束,就很麻烦。Promise.all()方法无法做到这一点。

1
2
3
4
5
6
7
8
9
const urls = [ /* ... */ ];
const requests = urls.map(x => fetch(x));

try {
await Promise.all(requests);
console.log('所有请求都成功。');
} catch {
console.log('至少一个请求失败,其他请求可能还没结束。');
}

Promise.any()

ES2021 引入了Promise.any()方法。该方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例返回。只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态。

欢迎关注我的其它发布渠道