大家好,又见面了,我是你们的朋友全栈君。
目录
Promise的含义
Promise是一个容器,内部保存着某个未来才会结束的事件(通常是一个异步操作)的结果。Promise也是一个对象,可以通过这个对象获取异步操作的消息。
Promise的特点:
- 对象的状态不受外部影响,只有异步操作的结果才能决定状态。一共有三种状态:
pending
(进行中)、fulfilled
(成功的)和rejected
(失败的)。 - 对象的状态发生改变后,不会再变化,并且随时可以得到这个结果。对象的状态改变只有两种情况:
pending
=》fulfilled
,pending
=》rejected
。
Promise的缺点:
- 无法取消Promise,一旦创建就会立即执行。
- 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
- 当对象状态是pending时,无法得知当前进行到哪一步(刚刚开始还是即将完成)。
基本用法
- 创建Promise实例
const promise = new Promise(function(resolve, reject){
// ...some code
if (/*异步操作成功*/) {
resolve(value);
} else {
reject(error);
}
});
Promise构造函数接收一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由JavaScript引擎提供,不用自己部署。
resolve函数的作用:将Promise对象状态从“未完成”变为“成功”(pending=》resolved)。在异步操作成功时调用,并将异步操作的结果作为参数传递出去。
reject函数的作用:将Promise对象状态从“未完成”变为“失败”(pending=》rejected)。在异步操作失败时调用,将异步操作爆出的错误,作为参数传递出去。
- 调用Promise
Promise实例生成后,可以用then方法分别指定resolved状态和rejected状态的回调函数。
promise.then(function(value){
//success Promise对象的状态变为resolved时调用
}, function(error){
//failure Promise对象的状态变为rejected时调用,该函数为可选参数
});
- reject函数和resolve函数参数
reject函数的参数通常是Error对象的实例。
resolve函数的参数除了正常的值意外,还可能是另一个Promise实例。
Promise.prototype.then()
Promise实例具有then
方法,即then
方法是定义在原型对象Promise.prototype
上的。它的作用是为Promise实例添加状态改变时的回调函数。then方法的第一个参数是resolved
状态的回调函数,第二个参数(可选)是rejected
状态的回调函数。
then
方法返回的是一个新的Promise实例(不是原来的那个Promise实例)。因此可以采用链式写法,即then
方法后面再调用另一个then
方法。
// 第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数,回调函数可以采用箭头函数形式,更加简洁
getJSON("/posts.json").then(function(json){
return json.post;
}).then(function(post){
//...
});
Promise.prototype.catch()
Promise.prototype.catch()
方法是.then(null, rejection)
或.then(undefined, rejection)
的别名,用于指定发生错误时的回调函数。
getJSON("/posts.json").then(fuction(posts){
//...
}).catch(function(error){
// 处理getJSON 和 前一个回调函数运行时发生的错误
});
如果没有使用catch()
方法指定错误处理的回调函数,Promise对象抛出的错误不会传递到外层代码,即不会有任何反应。
Promise.prototype.finally()
finally()
方法用于指定不管Promise对象最后状态如何,都会执行的操作。该方法是ES2018引入标准的。
finally
方法的回调函数不接受任何参数,这意味着它不知道前面的Promise实例的状态,这表明,finally
方法里面的操作,应该是和状态无关的,不依赖于Promise的执行结果。
finally
本质上是then
方法的特例。
Promise.all()
Promise.all()
方法用于将多个Promise实例,包装挣一个新的Promise实例。
const p = Promise.all([p1, p2, p3]);
参数:Promise.all()
方法接收一个数组作为参数,p1、p2、p3
都是Promise实例,如果不是,则调用Promise.resolve
方法,将参数转化为Promise实例。
Promise.all()
方法的参数可以不是数组,但必须具有Iterator接口。
新实例:p
的状态由p1、p2、p3
决定,有两种情况:
fulfilled
。只有p1、p2、p3
的状态都变成fulfilled
,p
的状态才会变成fulfilled
,此时,它们的返回值组成一个数组,传递给p
的回调函数。rejected
。只要p1、p2、p3
之中有一个被rejected
,p
的状态就变成了rejected
,此时,第一个被reject
的实例的返回值,会传递给p
的回调函数。
注意:如果作为参数的Promise实例,自己定义了catch
方法,那么它一旦被rejected
,并不会触发Promise.all()
的catch
方法。
const p1 = new Promise((resolve, reject) => {
resolve('hello');
})
.then(result => result)
.catch(e => e);
const p2 = new Promise((resolve, reject) => {
throw new Error('报错了');
})
.then(result => result)
.catch(e => e);
Promise.all([p1, p2])
.then(result => console.log(result))
.catch(e => console.log(e));
// ["hello", Error: 报错了]
原因:p1
会resolved
,p2
会rejected
,但是p2
有自己的catch
方法,该方法返回的是一个新的Promise实例,p2
实际上是这个实例。该实例执行完catch
方法后,也会变成resolved
,导致Promise.all()
方法参数里面的两个实例都会resolved
,因此会调用then
方法指定的回调函数,而不会调用catch
方法指定的回调函数。
如果p2
没有自己的catch
方法,就会调用Promise.all()
的catch
方法。
Promise.race()
Promise.race()方法同样是将多个Promise实例,包装成一个新的Promise实例。
const p = Promise.race([p1, p2, p3]);
新实例:只要p1、p2、p3
之中有一个实例率先改变状态,p
的状态就跟着改变。率先改变的Promise实例的返回值,就传递给p
的回调函数。
参数:和Promise.all()方法一样,如果不是Promise实例,就会调用Promise.resolve()方法转化。
const p = Promise.race([
fetch('/resource-that-may-take-a-while'),
new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('request timeout')), 5000)
})
]);
p
.then(console.log)
.catch(console.error);
上面代码中,如果5秒内fetch
方法无法返回结果,结果p
的状态就会变为rejected
,从而触发catch
方法指定的回调函数。
Promise.allSettled()
Promise.allSettled()
方法接收一组Promise实例作为参数,包装成一个新的Promise实例。只有等到所有这些参数实例都返回结果,无论是fulfilled
还是rejected
,包装实例才会结束。该方法由ES2020引入。
新实例:返回新的Promise实例,一旦结束,状态总是fulfilled
,不会变成rejected
。状态变成fulfilled
后,Promise的监听函数接收到的参数是一个数组,每个成员对应传入一个Promise.allSettled()
的Promise实例。
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 }
// ]
使用Promise.allSettled()
方法可以确定所有传入的Promise实例的异步操作是否结束了。
Promise.any()
Promise.any()方法接收一组Promise实例作为参数,包装成一个新的Promise实例。
新实例:新实例的状态由所有参数实例决定,有两种情况:
fulfilled
。只要参数实例有一个变成fulfilled
状态,包装实例就会变成fulfilled
。rejected
。如果所有参数实例都变成rejected
状态,包装状态就会变成rejected
状态。
var resolved = Promise.resolve(42);
var rejected = Promise.reject(-1);
var alsoRejected = Promise.reject(Infinity);
Promise.any([resolved, rejected, alsoRejected]).then(function (result) {
console.log(result); // 42
});
Promise.any([rejected, alsoRejected]).catch(function (results) {
console.log(results); // [-1, Infinity]
});
Promise.resolve()
Promise.resolve()
方法可以把现有对象转为Promise对象。
Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))
- 参数是一个 Promise 实例
不做任何修改。 - 参数是一个
thenable
对象
thenable
对象是指具有then
方法的对象。如下:
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
};
转化为Promise对象,然后执行thenable
对象的then
方法。
3. 参数不是具有then方法的对象,或根本就不是对象
const p = Promise.resolve('Hello');
p.then(function (s){
console.log(s)
});
// Hello
返回的Promise实例一生成就是resolved,所以回调函数会立即执行。
4. 不带有任何参数
Promise.resolve()方法允许调用时不带参数,直接返回一个resolved状态的Promise对象(没有参数)。
const p = Promise.resolve();
p.then(function () {
// ...
});
Promise.reject()
Promise.reject(reason)
方法也会返回一个新实例,该实例状态为rejected
。
const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))
p.then(null, function (s) {
console.log(s)
});
// 出错了
注意:Promise.reject()
方法的参数,会原封不动地作为reject
的理由,变成后续方法的参数。这一点与Promise.resolve
方法不一致。
如果传入一个thenable
对象,那回调函数的参数就是这个对象。
应用
- 加载图片
加载完,根据成功与否(Promise状态变化),去回调函数,进行后续操作。
const preloadImage = function (path) {
return new Promise(function (resolve, reject) {
const image = new Image();
image.onload = resolve;
image.onerror = reject;
image.src = path;
});
};
- Generator函数与Promise的结合
Promise.try()
需求:不区分函数f
是同步函数还是异步操作,但是想用Promise来处理它。因为这样就可以不管f
是否包含异步操作,都用then
方法指定下一步流程,用catch
方法处理f
抛出的错误。
一般写法:
const f = () => console.log('now');
Promise.resolve().then(f);
console.log('next');
// next
// now
一般写法缺点:如果f是同步函数,它会在本轮事件循环的末尾执行。
改进写法1:
const f = () => console.log('now');
(async () => f())(); // 立即执行的匿名函数
console.log('next');
// now
// next
第二行的匿名函数立即执行里面的async函数,如果f
同步就会得到同步结果,如果f
异步,就可以用then
指定下一步,catch
捕获错误。如果不使用catch
方法捕获错误,async() => f()
会吃掉f()
抛出的错误。
改进写法2:
const f = () => console.log('now');
(
() => new Promise(
resolve => resolve(f())
)
)();
console.log('next');
// now
// next
同样是立即执行的匿名函数,创建一个新的Promise实例,调用resolve
方法,把f
函数作为参数传入。这种情况下,同步函数也是同步执行的。
改进方法3:
const f = () => console.log('now');
Promise.try(f);
console.log('next');
// now
// next
Promise.try()
方法替代上面两种写法,为所有操作提供了统一的处理机制,还可以更好地管理异常。
管理异常:
一个Promise对象抛出异步错误,可以用catch
方法捕获,如下:
database.users.get({
id: userId})
.then(...)
.catch(...)
但如果该对象抛出了同步错误,就需要使用try...catch
去捕获。
try {
database.users.get({
id: userId})
.then(...)
.catch(...)
} catch (e) {
// ...
}
使用Promise.try()
方法生成的实例,可以统一调用promise.catch()
捕获所有同步和异步的错误。
Promise.try(() => database.users.get({
id: userId}))
.then(...)
.catch(...)
Promise.try
模拟try
代码块,promise.catch
模拟catch
代码块。
学习资料
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/139844.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...