基础概念
Promise 是异步编程的一种解决方案。
有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。
特点
1.对象的状态不受外界影响 (3种状态)
- 1.pending (进行中)
- 2.Fulfilled (已成功)
- 3.Rejected (已失败) 2.一旦状态改变就不会再改变 (成功或失败)
- Pending -> Fulfilled
- Pending -> Rejected
3.用法
var pro = new Promise(function(resolve, reject) {
if(true) {
resolve(value)
} else {
reject(error)
}
})
Promise构造函数接受一个函数作为参数,该函数的两个参数分别是 resolve 和 reject,他们是两个函数,由 js 引擎提供,不用自己部署。 resolve 是由 未完成 -> 已完成的状态,在异步操作成功时调用,并将异步操作的结果作为参数传递出去。rejected 是由未完成 -> 失败的状态,在异步操作失败是调用,resolve 是由 未完成 -> 已完成的状态,在异步操作成功时调用,并将异步操作的结果作为参数传递出去。rejected 是由未完成 -> 失败的状态,在异步操作失败是调用,并将异步操作的结果作为参数传递出去。
- promise 实例生成以后,可以用 then 方法分别指定 resolved 状态和 rejected 状态的回调函数
- Promise 新建后就会立即执行。
var promise = new Promise(function(resolve, reject) {
if(true) {
resolve(value)
} else {
reject(error)
}
})
promise.then(function(res) {
// 成功回调
},function(error) {
// 失败回调
})
const p1 = new Promise(function(res,reject) {
// reject(new Error('fail'))
res('小仙女')
console.log(123) // 123
})
const p2 = new Promise(function(res,reject) {
res(p1)
})
p2.then((res)=>{
console.log(res,1) // 小仙女
})
.catch((res)=>{
console.log(res,2)
})
p1 , p2 都是 promise 实例,但是 p2 的 resolve 方法将 p1 作为参数传递过去,既一个异步操作的结果是返回另一个异步操作.上诉看出p1的状态决定了p2的状态
注意 调用 resolve 和 reject 并不会终结的参数函数的执行
4.Promise.prototype.then()
- Promise 实例具有 then 方法,也就是说,then的方法是定义在原型对象Promise.prototype 上的,它的作用是为 Promise 实例添加改变时的回调函数.then 方法的第一个参数是 resloved状态的回调参数,第二个参数是 rejected状态的回调函数,他妈都是可选的.
- then 方法返回的是一个新的 Promise 实例,不是原来的那个 Promise 实例.因此可以进行链式调用.第一个回调函数完成以后,会将返回结果作为参数,参入给第二个回调函数.
new Promise((resolve,reject)=>{
resolve(1)
}).then(()=>{
return '小仙女'
}).then((res)=>{
console.log(res) // 小仙女
})
5.Promise.prototype.catch()
- Promise.prototype.catch 方法是.then(null,rejection) 或.then(undefined,rejection) 的别名,用于指定发生错误时的回调函数.
let getData = new Promise((res)=>{
res()
})
getData.then((res)=>{
// 成功回调
}).catch((error)=>{
// 发生错误
})
暂举 两种错误方式捕获方式
const promise = new Promise(function(resolve, reject) {
throw new Error('test');
});
promise.catch(function(error) {
console.log(error);
});
// Error: test
const promise = new Promise(function(resolve, reject) {
reject(new Error('test'));
});
promise.catch(function(error) {
console.log(error); // test
});
如果Promise 的状态已经是 resolved,在抛出错误是无效的,也不会被捕捉上 Promise 内部的错误不会影响到Promise 外部的代码,通俗说 Promise 会吃掉错误.
const promise = new Promise(function(resolve, reject) {
resolve('ok');
throw new Error('test'); // 无效的
});
promise
.then(function(value) { console.log(value) })
.catch(function(error) { console.log(error) }); // 捕获不到
// ok
6.Promise.prototype.finally()
- finally() 方法用于指定 Promise 对象最后状态如何,都会执行的操作。
7.Promise.all()
- 该方法用于将多个Promise实例,包装成一个新的 Promise 实例。
const p = Promise.all([p1,p2,p3])
promise.all() 接受数组作为参数,Promise.all()方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。
const p1 = Promise.resolve(1)
const p2 = Promise.resolve(2)
const p3 = Promise.reject(3)
const p = Promise.all([p1,p2,p3])
p.then((res)=>{
console.log(res) // 报错
})
1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
const p1 = Promise.resolve(1)
const p2 = Promise.resolve(2)
const p3 = Promise.resolve(3)
const p =Promise.all([p1,p2,p3])
p.then((res)=>{
console.log(res) // [1,2,3]
})
2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
const p1 = Promise.reject(1)
const p2 = Promise.resolve(2)
const p3 = Promise.resolve(3)
const p = Promise.all([p1,p2,p3])
.then((res)=>{
console.log(res,123)
})
.catch((res)=>{
console.log(res,'qqq') // 1,'qqq'
})
- 注意,如果作为参数的Promise实例,自己定义catch方法,那么他一旦被 rejected,并不会出发Peomise.all() 的 catch方法。
const p1 = new Promise((res,reject)=>{
reject(1)
}).catch(e=>e)
const p2 = Promise.resolve(2)
const p3 = Promise.resolve(3)
const p = Promise.all([p1,p2,p3])
.then((res)=>{
console.log(res,123) // [1, 2, 3] 123
})
.catch((res)=>{
console.log(res,'qqq')
})
上述代码中 p1 被 rejected,但是p1也有自己的 catch 方法,该方法返回一个新的 Peimise 实例,p1 指向的实际是这个实例,该实例执行完catch方法后,也会变成resolved,p2,p3 都是 resloved,所以 Promise.all()方法参数里面的两个实例都是 preslved,因此会调用then的方法,不会调用 catch 方法
- 如果
p1没有自己的catch, 就是调用Promise.all()的catch方法,如果p1没有自己的catch方法,就是Promise.all()的catch方法了
8.Promise.race()
·Promise.race() 方法同样将多个Promise 实例,包装成一个新的 Promise 实例
const p1 = new Promise((res,reject)=>{
res(1)
})
const p2 = Promise.resolve(2)
const p3 = Promise.resolve(3)
const p = Promise.race([p1,p2,p3])
.then((res)=>{
console.log(res) // 1
})
.catch((res)=>{
console.log(res,'qqq')
})
p1状态改变 就是立马调用 p 的 then 的方法,如果p1是reject,里面调用p的catch方法
9.Promise.allStetied()
- 有时候,我们希望等到一组异步操作都结束了,不管每一个操作是成功还是失败,再进行下一步操作。但是,现有的 Promise 方法很难实现这个要求。
- ES2020 引入了
Promise.allSettled()方法,用来确定一组异步操作是否都结束了(不管成功或失败)。所以,它的名字叫做”Settled“,包含了”fulfilled“和”rejected“两种情况。 - Promise.allSetted() 方法接受一个数组作为参数,数组的每一个成员都是 Promise 对象,并返回一个新的 Promise 对象。只有等待数组的所有 Peomise 对象状态改变,成功或失败,返回的 Promise 对象才会发生状态变更。
- 该方法返回的新的
Promise实例,一旦状态变更,状态总是fulfilled,不会rejected。状态变成fulfilled后,它的回调函数会接受到一个数组作为参数,该数组的每一个成员对应前面数组的每一个Promise对象。
const resolved = Promise.resolve(5);
const rejected = Promise.reject(7);
const allSettledPromise = Promise.allSettled([resolved, rejected]);
allSettledPromise.then(function (results) {
console.log(results);
// (2) [{…}, {…}]
// 0: {status: 'fulfilled', value: 42}
// 1: {status: 'rejected', reason: -1}
// length: 2
// [[Prototype]]: Array(0)
});
上面的代码中,**Promise.allSettled()**的返回值 allSettledPromise,状态只能变成 fulfilled。它的回调函数接受到的参数事数组 results.该数组的每一个成员都是一个对象,对应传入 Promise.allSettled() 的数组里面的两个 Promise 对象。
- results 的每一个成员都是一个对象,对象的格式事固定的
// 异步操作成功时
{status: 'fulfilled', value: value}
// 异步操作失败时
{status: 'rejected', reason: reason}
10.Promise.any()
ES2021 引入了Promise.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);
})
只有参数实例有一个变成 fulfilled 状态,包装实例就会变成 fulfilled 状态,如果所有参数都变成 rejected 状态,包装实例就会变成 rejected 状态
Promise.any()跟Promise.race()方法很像,只有一点不同,就是Promise.any()不会因为某个 Promise 变成rejected状态而结束,必须等到所有参数 Promise 变成rejected状态才会结束。
11 Promise.resolve()
有时需要将现有的对象转为 Promise 对象,Promise.resolve() 方法就起到这个作用。
Promise.resolve('foo')
new Promise(resolve => ('foo'))
写法相等
Promise 的方法参数分成4种情况
- 参数是一个 Promise 实例 如果参数事 Promise 实例,那么Promise.resolve 将不做任何修改,原封不动的返回这个实例。
- 参数是一个 thenable 对象 thenable对象指的是具有then方法的对象
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
};
Promise.sesolve() 方法会将这个对象转为Promise对象,然后就立即执行 thenable 对象方法的 then() 方法
let thenable = {
then: function(resolve, reject) {
console.log(123)
resolve(42);
}
};
let p1 = Promise.resolve(thenable);
p1.then(function (value) {
console.log(value); // 42
});
上面代码中先输出 123 然后 47,thenable对象的then()方法执行后,对象p1的状态就变为resolved,从而立即执行最后那个then()方法指定的回调函数,输出42。
- 参数不是具有 then() 方法的对象,或根本不是对象
如果参数是一个原始值,或者是一个不具有
then()方法的对象,则Promise.resolve()方法返回一个新的 Promise 对象,状态为resolved。
const p = Promise.resolve('Hello');
p.then(function (s) {
console.log(s)
});
// Hello
上面代码生成一个新的 Promise 对象的实例p。由于字符串Hello不属于异步操作(判断方法是字符串对象不具有 then 方法),返回 Promise 实例的状态从一生成就是resolved,所以回调函数会立即执行。Promise.resolve()方法的参数,会同时传给回调函数
- 不带任何参数
Promise.resolve()方法允许调用时不带参数,直接返回一个resolved状态的 Promise 对象。
12.Promise.reject()
Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected。
const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))
p.then(null, function (s) {
console.log(s)
});
// 出错了
上面代码生成一个 Promise 对象的实例p,状态为rejected,回调函数会立即执行。
Promise.reject()方法的参数,会原封不动地作为reject的理由,变成后续方法的参数。