一、传统异步请求的处理方式
这种回调的方式有很多的弊端:
- 如果是我们自己封装的requestData,那么我们在封装的时候必须要自己设计好回调函数、回调函数的名称、回调函数的使用;
- 如果我们使用的是别人封装的requestData或者一些第三方库, 那么我们必须去看别人的源码或者文档, 才知道它这个函数需要怎么去获取到结果、这个函数到底怎么用;
function requestData(url, sucessCallback, failtrueCallback) {
setTimeout(() => {
if (url === 'yzh') {
const list = ['ace', 'sabot', 'luffy'];
sucessCallback(list);
} else {
const message = '请求失败!';
failtrueCallback(message);
}
}, 2000)
};
调用成功/失败结果:
requestData('yzh', res => {
console.log(res); //[ 'ace', 'sabot', 'luffy' ]
}, err => {
console.log(err);
});
requestData('abc', res => {
console.log(res);
}, err => {
console.log(err); //请求失败!
});
二、什么是Promise?
- Promise是一个类,可以翻译成 许诺、承诺、答应、保证;
- 当我们需要给予调用者一个承诺:待会儿我会给你回调数据时,就可以创建一个Promise的对象;
- 在通过new创建Promise对象时,我们需要传入一个回调函数,这个函数被称之为"executor";
- 这个回调函数会被立即执行,并且被传入另外两个回调函数resolve、reject;
- 当我们调用resolve回调函数时,会执行Promise对象的then方法传入的回调函数;
- 当我们调用reject回调函数时,会执行Promise对象的catch方法传入的回调函数;
2.1. promise的三种状态
TIP:
Promise状态一旦确定下来, 那么就是不可更改的(锁定);
在我们调用resolve的时候,那么会将该Promise的状态变成 兑现(fulfilled);
在之后我们去调用reject时,已经不会有任何的响应了(并不是这行代码不会执行,而是无法改变Promise状态);
- 待定(pending):当执行executor中的代码时,处于该状态;
- 已兑现(fulfilled):意味着操作成功完成,执行了resolve时,处于该状态;
- 已拒绝(rejected):意味着操作失败,执行了reject时,处于该状态;
new Promise((resolve, reject) => {
// pending
console.log("pending中----------")
resolve() // fulfilled
reject() // rejected
console.log("++++++++++++")
}).then(res => {
console.log(res)
}, err => {
console.log(err)
})
三、 promise异步请求的处理方式
- resolve: 回调函数, 在成功时, 回调resolve函数;
- reject: 回调函数, 在失败时, 回调reject函数;
function requestData(url) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (url === 'yzh') {
const list = ['ace', 'sabot', 'luffy'];
resolve(list);
} else {
const message = '请求失败!';
reject(message);
}
}, 2000)
})
};
then方法传入的两个回调函数:
- 第一个回调函数, 会在Promise执行resolve函数时, 被回调
- 第二个回调函数, 会在Promise执行reject函数时, 被回调
const promise = requestData('yzh');
promise.then(res => {
console.log('请求成功:', res); //请求成功:['ace', 'sabot', 'luffy']
}, err => {
console.log('请求失败:', err);
});
catch方法传入的回调函数, 会在Promise执行reject函数时, 被回调
const promise = requestData('abc');
promise.then(res => {
console.log('请求成功:', res);
}, err => {
console.log('请求失败:', err); //请求失败: 请求失败!
});
promise.catch(err => {
console.log('catch:', err); //catch: 请求失败!
});
四、resolve参数
4.1. 普通的值或对象
- 如果resolve传入一个普通的值或者对象,那么这个值会作为then回调的参数;
- 状态是:pending -> fulfilled / pending -> rejected;
new Promise((resolve, reject) => { resolve({ name: 'yzh', age: 18 }); // reject('失败'); }).then(res => { console.log('res:', res); // res: { name: 'yzh', age: 18 } }, err => { console.log('err:', err); // err: 失败 });
4.2. 传入一个Promise
- 如果resolve中传入的是另外一个Promise,那么这个新Promise会决定原Promise的状态;
- 当前的Promise的状态会由传入的Promise来决定,相当于状态进行了移交;
const newPromise = new Promise((resolve, reject) => { resolve('pending -> fulfilled'); // reject('失败'); }); new Promise((resolve, reject) => { resolve(newPromise); }).then(res => { console.log('res:', res); // res:pending -> fulfilled }, err => { console.log('err:', err); // err: 失败 });
4.3. 传入thenable对象
- 如果resolve中传入的是一个对象,并且这个对象有实现then方法,那么会执行该then方法,并且根据then方法的结果来决定Promise的状态;
new Promise((resolve, reject) => { resolve({ then: function(resolve, reject) { resolve('thenable - ok'); // reject('thenable - no'); } }); }).then(res => { console.log('res: ', res) // res:thenable - ok }, err => { console.log('err: ', err) // err: thenable - no });
4.5. 补充
4.5.1 转成Promise对象
function foo() {
const obj = { name: 'joo', age: 18 };
return new Promise(resolve => {
resolve(obj);
});
};
foo().then(res => {
console.log(res); // { name: 'joo', age: 18 }
});
4.5.2 resolve方法
- Promise.resolve的用法相当于new Promise,并且执行resolve操作;
const prme = Promise.resolve({ name: 'kll' }); //相当于 /* const prme = new Promise(resolve => { resolve({ name: 'kll' }); }); */ prme.then(res => { console.log(res) // { name: 'kll' } });
4.5.3 reject方法
- reject方法类似于resolve方法,只是会将Promise对象的状态设置为reject状态;
- Promise.reject的用法相当于new Promise,并且执行reject操作;
const promise = Promise.reject("rejected message"); // 相当于 /* const promise2 = new Promsie((resolve, reject) => { reject("rejected message"); }); */
- Promise.reject传入的参数无论是什么类型,都会直接作为reject状态的参数传递到catch的;
// 注意: 无论传入什么值都是一样的 const promise = Promise.reject(new Promise(() => {})); promise.then(res => { console.log("res:", res) }).catch(err => { console.log("err:", err) //err: Promise { <pending> } });
warning:不要在解析为自身的 thenable 上调用
Promise.resolve
。这将导致无限递归,因为它试图展平无限嵌套的 promise;
例如下例代码
let thenable = {
then: (resolve, reject) => {
resolve(thenable);
}
};
Promise.resolve(thenable); //这会造成一个死循环
五、对象方法
TIP:
查看Promise有哪些对象方法:
console.log(Object.getOwnPropertyDescriptors(Promise.prototype))
;then、catch、finally方法是Promise对象上的方法:
它们其实是放在Promise的原型上的
Promise.prototype.then/catch/finally
;
Object.getOwnPropertyDescriptors()
方法用来获取一个对象的所有自身属性的描述符;
5.1. then方法
前面:
const promise = new Promise((resolve, reject) => {
resolve("hahaha");
//reject("henghengheng");
});
5.1.1 接受两个参数
- fulfilled的回调函数:当状态变成fulfilled时会回调的函数;
- rejected的回调函数:当状态变成rejected时会回调的函数;
promise.then(res => { console.log('res: ', res) }, err => { console.log('err: ', err) }); //等同于 promise.then(res => { console.log('res: ', res) }).catch(err => { console.log('err: ', err) });
5.1.2 多次调用
- 同一个Promise可以被多次调用then方法
- 每次调用我们都可以传入对应的fulfilled回调;
- 当前面我们的resolve方法被回调时,所有的then方法传入的fulfilled回调函数都会被调用;
promise.then(res => { console.log("res1:", res) //res1: hahaha }); promise.then(res => { console.log("res2:", res) //res2: hahaha }); promise.then(res => { console.log("res3:", res) //res3: hahaha });
5.1.3 返回值
- then方法本身也是有返回值的, 它的返回值是Promise,所以我们可以进行链式调用;
- then方法返回的Promise的三种状态;
- 当then方法中的回调函数本身在执行的时候,那么它处于pending状态;
- 当then方法中的回调函数返回一个结果时,那么它处于fulfilled状态,并且会将结果作为resolve的参数;
- 1> 如果我们返回的是一个普通值(数值/字符串/普通对象/undefined), 那么这个普通的值被作为一个新的Promise的resolve值;
promise.then(res => { console.log('res: ', res); //res: hahaha return "aaaaaa" }).then(res => { console.log("res: ", res); //res: aaaaaa return "bbbbbb" }).then(res => { console.log('res: ', res); //res: bbbbbb })
- 2> 如果我们返回的是一个Promise;
promise.then(res => { return new Promise((resolve, reject) => { setTimeout(() => { resolve(111111) }, 2000) }) }).then(res => { console.log("res: ", res) //res: 111111 })
- 3> 如果返回的是一个对象, 并且该对象实现了thenable;
promise.then(res => { return { then: function(resolve, reject) { resolve(222222) } } }).then(res => { console.log("res: ", res) //res: 222222 });
- 1> 如果我们返回的是一个普通值(数值/字符串/普通对象/undefined), 那么这个普通的值被作为一个新的Promise的resolve值;
- 当then方法抛出一个异常时,那么它处于rejected状态;
promise.then(res => { throw new Error('error message!'); }).catch(err => { console.log(err); //Error: error message! });
5.2. catch方法
5.2.1 多次调用
- 一个Promise的catch方法是可以被多次调用的:
- 每次调用我们都可以传入对应的rejected回调;
- 当Promise的状态变成rejected的时候,这些回调函数都会被执行;
const promise = new Promise((resolve, reject) => { reject('rejected status'); }); promise.catch(err => { console.log('err: ', err) //err: rejected status }); promise.catch(err => { console.log('err: ', err) //err: rejected status });
5.2.2 抛出异常
前面:
const promise = new Promise((resolve, reject) => {
// 1.
// reject('reject status fn ');
// throw new Error('reject status throw ');
// 2.
resolve();
});
- 当executor抛出异常时,也是会调用错误捕获的回调函数的;
// 1. promise.then(undefined, err => { console.log('err:', err); //err: reject status fn });
- 通过catch方法来传入错误(拒绝)捕获的回调函数;
// 1. promise.catch(err => { console.log('err:', err); });
- 捕获级(优先捕获promise这个对象里的异常,无则去then里捕获异常);
// 2. promise.then(res => { throw new Error('then throw error') }).catch(err => { console.log('err:', err); //err: Error: then throw error });
5.2.3 返回值
- 实际上catch方法也是会返回一个Promise对象的,所以catch方法后面我们可以继续调用then方法或者catch方法;
const promise3 = new Promise((resolve, reject) => { reject('2222') }); promise3.then(res => {}).catch(err => { console.log('err', err); //err 2222 return 'catch return value'; // 等同于 // return new Promise(resolve => resolve('catch return value')); // 所以返回值会走then; // 除非返回new promise使用reject或者抛出异常会走到catch; // return new Promise((resolve, reject) => reject('new error')); // throw new Error('throw new err'); }).then(res => { console.log('res2', res); //res2 catch return value }).catch(err => { // console.log('err2', err); //err2 new error console.log('err2', err); //err2 Error: throw new err });
5.3. finally方法
TIP:
这是为在Promise是否成功完成后都需要执行的代码提供了一种方式,避免了同样的语句需要在then()和catch()中各写一次的情况;
- finally可以翻译成 最后、最终;
- finally是在ES9(ES2018)中新增的一个特性:表示无论Promise对象无论变成fulfilled还是rejected状态,最终都会被执行的回调函数;
- finally方法是不接收参数的,因为无论前面是fulfilled状态,还是rejected状态,它都会执行;
const promise = new Promise((resolve, reject) => { // resolve('resolve message'); reject('reject status'); }); promise.then(res => { console.log(res); }).catch(err => { console.log(err); }).finally(() => { console.log('无论resolve还是reject都会执行'); });
🍚还有
六、类方法
前面写过的then、catch、finally方法都属于Promise的实例方法,都是存放在Promise的prototype上的;
resolve、reject是属于Promise的类方法(类的静态方法);
6.1. all方法
- 它的作用是将多个Promise包裹在一起形成一个新的Promise;
- 新的Promise状态由包裹的所有Promise共同决定:
- 当所有的Promise状态变成fulfilled状态时,新的Promise状态为fulfilled,并且会将所有Promise的返回值组成一个数组;
const proRe1 = new Promise((resolve, reject) => { setTimeout(() => { resolve('1111') }, 1000) }); const proRe2 = new Promise((resolve, reject) => { setTimeout(() => { resolve('222') }, 2000) }); const proRe3 = new Promise((resolve, reject) => { setTimeout(() => { resolve('3333') }, 3000) }); Promise.all([proRe1, proRe2, proRe2]).then(res => { console.log(res); //[ '1111', '222', '3333' ] }).catch(err => { console.log(err); });
- 当有一个Promise状态为rejected时,新的Promise状态为rejected,并且会将第一个reject的返回值作为参数;
const proJe1 = new Promise((resolve, reject) => { setTimeout(() => { reject('1111') }, 1000) }); const proJe2 = new Promise((resolve, reject) => { setTimeout(() => { reject('222') }, 2000) }); const proJe3 = new Promise((resolve, reject) => { setTimeout(() => { resolve('3333') }, 3000) }); Promise.all([proJe1, proJe2, proJe3]).then(res => { console.log(res); }).catch(err => { console.log(err); //1111 });
- 当所有的Promise状态变成fulfilled状态时,新的Promise状态为fulfilled,并且会将所有Promise的返回值组成一个数组;
6.2. allSettled方法
all方法缺陷:
当有其中一个Promise变成rejected状态时,新Promise就会立即变成对应的rejected状态;
那么对于resolve的,以及仍然处于pending状态的Promise,我们是获取不到对应的结果的;
- 在ES11(ES2020)中,添加了新的API Promise.allSettled:
- 该方法会在所有的Promise都有结果(settled),无论是fulfilled,还是reject时,才会有最终的状态;
- 并且这个Promise的结果一定是fulfilled的(最终都会走到then);
const proAllS1 = new Promise((resolve, reject) => { setTimeout(() => { resolve('1111') }, 1000) }); const proAllS2 = new Promise((resolve, reject) => { setTimeout(() => { reject('222') }, 2000) }); const proAllS3 = new Promise((resolve, reject) => { setTimeout(() => { resolve('3333') }, 3000) }); Promise.allSettled([proAllS1, proAllS2, proAllS3]).then(res => { console.log('res: ', res); /* res: [ { status: 'fulfilled', value: '1111' }, { status: 'rejected', reason: '222' }, { status: 'fulfilled', value: '3333' } ] */ }).catch(err => { console.log('err: ', err); });
- allSettled的结果是一个数组,数组中存放着每一个Promise的结果,并且是对应一个对象的;
- 这个对象中包含status状态,以及对应的value值;
6.3. race方法
- race是比赛,竞争,角逐的意思,表示多个Promise相互竞争,谁先有结果,那么就使用谁的结果;
- 如果有一个Promise有了结果,我们就希望决定最终新Promise的状态,那么可以使用race方法:
const proRa1 = new Promise((resolve, reject) => { setTimeout(() => { resolve('1111'); }, 1000) }); const proRa2 = new Promise((resolve, reject) => { setTimeout(() => { reject('222'); }, 2000)//500 }); const proRa3 = new Promise((resolve, reject) => { setTimeout(() => { resolve('3333'); }, 3000) }); //race是竞技、竞赛的意思,表示多个Promise相互竞争,谁先有结果,那么就使用谁的结果; Promise.race([proRa1, proRa2, proRa3]).then(res => { console.log('res: ', res); //res: 1111 }).catch(err => { console.log('err: ', err); });
6.4. any方法
- any方法是ES12(2021)中新增的方法,和race方法是类似的;
- any方法会等到一个fulfilled状态,才会决定新Promise的状态;
const p1 = new Promise((resolve, reject) => { setTimeout(() => { resolve('1111') }, 1000) }); const p2 = new Promise((resolve, reject) => { setTimeout(() => { reject('222') }, 500) }); const p3 = new Promise((resolve, reject) => { setTimeout(() => { resolve('3333') }, 3000) }); Promise.any([p1, p2, p3]).then(res => { console.log('res: ', res); //res: 1111 }).catch(err => { console.log('err: ', err); });
- 如果所有的Promise都是reject的,那么也会等到所有的Promise都变成rejected状态;
- 如果所有的Promise都是reject的,那么会报一个AggregateError的错误;
const p1 = new Promise((resolve, reject) => { setTimeout(() => { reject('1111') }, 1000) }); const p2 = new Promise((resolve, reject) => { setTimeout(() => { reject('222') }, 500) }); const p3 = new Promise((resolve, reject) => { setTimeout(() => { reject('3333') }, 3000) }); Promise.any([p1, p2, p3]).then(res => { console.log('res: ', res); }).catch(err => { console.log('err meesage: ', err); //err meesage: [AggregateError: All promises were rejected] // [聚合错误:所有承诺都被拒绝] });
- 如果所有的Promise都是reject的,那么会报一个AggregateError的错误;
- any方法会等到一个fulfilled状态,才会决定新Promise的状态;
通过终端 node index.js 运行代码,其中Node版本14.及以下使用any方法,会报错:
TypeError: Promise.any is not a function
我使用的是16.版本,可以正常获取运行结果;
Promise.any()
方法依然是实验性的,尚未被所有的浏览器完全支持;