复习Promise知识,关于它的概念,使用方式等,还有注意事项。 参考依然是阮一峰老师的 ESCMAScript6入门 es6.ruanyifeng.com/ ,写它的原因是在项目中能经常的使用到Promise做异步处理,,但对它并没有系统性的了解。
Promise对象
概念
简单说下,Promise是一种js单线程的异步处理方案,在对后端的接口请求中我们经常会用到,相信大多数前端都对这个不陌生。ES6将Promise进行来便准规范。
状态
promise有三种状态,并且具备了状态不受干扰以及状态一旦改变就不会再变的特性。
三种状态分别是
- pending进行中
- fulfilled已成功
- rejected已失败 需要注意的是,一旦执行,中途就无法取消,如果不设置回调函数,Promise内部会抛出错误,而且在pending状态中,不知道会进展到哪个阶段。
使用方式
在前端的使用过程中,一般在请求的时候都会用到,有的用来封装请求,也有在store中使用,当然也是封装请求的。大致如下
function getUserInfo(data) {
return new Promise((resolve, reject) => {
// 接口的方法
if(true) {
resolve('返回参数')
} else {
reject('错误的信息')
}
})
}
getUserInfo(data).then(res => {
console.log(res)
}).catch(res => {
console.log(res)
})
这个应该是最常见的使用方式,还有就是Promise.all()的方式,会在后边写到。
下边一个异步加载图片的例子
function loadImageAsync(url) {
return new Promise((resolve, reject) => {
const image = new Image()
image.onload = function() {
resolve(image)
}
image.onerror = function() {
reject(new Error('Could not load image at '+ url))
}
image.src = url
})
}
这里比较重要的一点还是promise的已成功 和已失败两个回调,在得到图片路径的时候,使用resolve来返回正确的地址,错误的时候返回错误的地址。
执行过程
在promise中,一般是从进行中到已成功或已失败。在这个过程中是有回调地址的,如果当前回调地址为一个新的promise,那么当前这个promise的状态其实是取决于当前回调这个promise的状态的。
例如:
const p1 = new Promise((resolve, reject) => {
setTimeout(() => resolve('p1'), 3000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => resolve(p1), 1000)
})
p2.then(result => console.log(result,'result')) // p1 result
.catch(error => console.log(error,'error'))
当前这个例子似乎是看不出来的,p2是成功回调,p1也是成功回调。对比第二个例子就能清晰看出来了
const p1 = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error('错了')), 3000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => resolve(p1), 1000)
})
p2.then(result => console.log(result,'result'))
.catch(error => console.log(error,'error')) // Error: 错了
两个比较,我们就可以清晰认识到这一点。
then()和catch()
Promise 实例具有then方法,then方法是定义在原型对象Promise.prototype上的。then方法可以链式调用,通俗说就是葫芦娃救爷爷,一个接一个。
同样,promise.prototype上还有catch,上一个是处理回调的,这个是用来处理错误的。
两种等价的写法
// 写法一
const promise = new Promise(function(resolve, reject) {
try {
throw new Error('test');
} catch(e) {
reject(e);
}
});
promise.catch(function(error) {
console.log(error);
});
// 写法二
const promise = new Promise(function(resolve, reject) {
reject(new Error('test'));
});
promise.catch(function(error) {
console.log(error);
});
// 比较上面两种写法,可以发现reject()方法的作用,等同于抛出错误。
而且Promise 对象的错误会一直向后传递,直到被捕获
finally
finally()
方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。相对用的比较少,在特定环境下,finally 也会有奇效,具体看业务场景。
all()
Promise.all()
方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
这个方法,在nuxtjs做服务端渲染的时候会用的比较多一些。用的方式也很特别。打包一起搞,全部返回后才走then方法。 状态有两种:全部为fulfilled,状态就是fuifilled;如果有一个是rejected,那么状态就为rejected.
全部为fulfilled
const p1 = new Promise((resolve, reject) => {
resolve('p1');
})
.then(result => result)
.catch(e => e);
const p2 = new Promise((resolve, reject) => {
resolve('p2');
})
.then(result => result)
.catch(e => e);
Promise.all([p1, p2])
.then(result => console.log(result,'then')) // ["p1", "p2"] "then"
.catch(e => console.log(e,'catch'));
有一个是rejected
const p1 = new Promise((resolve, reject) => {
resolve('p1');
})
.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,'then'))
.catch(e => console.log(e,'catch'));
再看下面的例子
const p1 = new Promise((resolve, reject) => {
resolve('p1');
})
.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,'then'))
.catch(e => console.log(e,'catch'));
这个似乎我们之前所说的不一致,主要是因为p2中使用了catch, p2首先进入rejected,但是因为有自己的catch方法,方法会返回一个新的实例,p2指向的实际是这个实例。实例执行完成后也变为了resolved,因此p1,p2都进入了then方法。
race()
这个方法和all()相反,那个率先改变的 Promise 实例的返回值,就传递给回调函数
const p1 = new Promise((resolve, reject) => {
setTimeout(resolve, 1000, 'p1')
})
.then(result => result)
.catch(e => e);
const p2 = new Promise((resolve, reject) => {
resolve('p2');
})
.then(result => result)
.catch(e => e);
Promise.race([p1, p2])
.then(result => console.log(result,'then')) // p2 then
.catch(e => console.log(e,'catch'));
如果将定时器去掉,你会发现一直执行的都是p1
allSettled()
Promise.allSettled()
方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。只有等到所有这些参数实例都返回结果,不管是fulfilled
还是rejected
,包装实例才会结束。该方法返回的实例,状态总是fulfilled,不会变成rejected,接受的参数也是一个数组。
const promises = [
fetch('/api-1'),
fetch('/api-2'),
fetch('/api-3'),
];
await Promise.allSettled(promises);
removeLoadingIndicator();
any()
该方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例返回。
Promise.any([
fetch('https://v8.dev/').then(() => 'home'),
fetch('https://v8.dev/blog').then(() => 'blog'),
fetch('https://v8.dev/docs').then(() => 'docs')
]).then((first) => { // 只要有一个 fetch() 请求成功
console.log(first);
}).catch((error) => { // 所有三个 fetch() 全部请求失败
console.log(error);
});
以上就是回顾的所有知识点,有不足指出,还请多多指教!!!