什么是Promise
简介
Promise对象用于表示一个异步操作的最终完成(或者失败)及其结果值。它可以把异步操作最终的成功返回值或者失败原因和相对应的程序关联起来。这样就使得异步方法可以像同步方法那样返回值;但是异步方法不会立即返回最终的值,而是会返回一个Promise,在未来某个时刻把值交给使用者。通俗来说,Promise就是一个容器,里面存放着一个异步操作的状态。
- Ajax方式的异步调用在需要多个操作的时候,会导致多个回调函数嵌套,导致代码不够直观,称为"Callback Hell" (
回调地狱
)。 - Promise即是异
步编程的一种解决方案
,早在 ECMAScript 2015 (ES6)中便成为标准。 - 从语法层面讲,Promise是一个
对象
,用来获取异步操作的信息。
打印Promise:
可以看到Promise自己身上有all
、reject
、resolve
这几个方法,原型上有then
、catch
等同样很眼熟的方法。 这么说用Promise new出来的对象肯定就有then、catch方法。
参数
new Promise接收一个参数,是函数
,并且传入两个参数:resolve
,reject
,分别表示异步操作执行成功后的回调函数
和异步操作执行失败后的回调函数
。其实这里用"成功"和"失败"来描述并不准确,按照标准来讲,resolve是将Promise的状态置为fullfiled(成功),reject是将Promise的状态置为rejected(失败)。
三种状态
Promise是一个状态机,分为三种状态
pending
: 进行中,表示Promise还在执行阶段,还没有执行完成,也可以理解为初始化状态fulfilled
: 成功,表示Promise执行成功rejected
: 拒绝状态,表示Promise执行被拒绝,也就是失败
实例方法
- .then()
- .catch()
- .finally()
.then()
在执行reslove的时候,Promise的状态会变为fulfilled,会执行.then方法,then方法中接收的参数也是一个函数,函数中携带一个参数,参数是resolve返回的数据。
.catch()
在执行reject时,Promise状态从pending变为reject,就会执行catch方法,catch方法中接收的参数也是一个函数,函数中携带一个参数,为reject返回的数据。
Promise 对象的错误具有"冒泡"
性质,会一直向后传递,直到被捕获为止。
javascript
getJSON('/post/1.json').then(function(post) {
return getJSON(post.commentURL);
}).then(function(comments) {
// some code
}).catch(function(error) {
// 处理前三个Promise产生的错误
});
.finally()
finally方法不管Promise对象处于什么状态,都会执行finally方法。finally方法的回调函数不接受任何参数,也就是说,在使用finally方法的时候我们无法判断Promise对象的状态,这也就说明了finally方法的执行与Promise的状态无关,不依赖Promise的执行结果。
构造函数方法
- all()
- race()
- allSettled()
- resolve()
- reject()
- try()
Promise.all()
- Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
- 接受1个数组(迭代对象)作为参数,数组成员都应为Promise实例。
- 实例p的状态由p1、p2、p3 决定
- 只有 p1、p2、p3 的状态都变成 fulfilled,p的状态才会变成 fulfilled ,此时 p1、p2、p3 的返回值组成一个数组,传递给 p的回调函数
- 只要 p1、p2、p3 之中有一个被rejected,p的状态就变成 rejected ,此时第一个被 reject 的实例的返回值,会传递给 p的回调函数
- 注意如果作为参数的 Promise 实例,自己定义了 catch 方法,那么它一旦被 rejected ,并不会触发 Promise.all()的 catch 方法
- 如果作为参数的 Promise 实例 没有自己的 catch 方法,就会调用 Promise.all()的 catch 方法
成功的时候返回的是一个结果数组,而失败的时候则返回最先被 reject 失败状态的值
typescript
const p = Promise.all([p1, p2, p3]);
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:报错了]
Promise.race()
- Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例
- 接受1个数组(迭代对象)作为参数,数组成员都应为Promise实例。
- 实例p的状态由p1、p2、p3 决定
- 只要 p1、p2、p3 之中有一个实例率先改变状态,p 的状态就跟着改变率先改变的 Promise 实例的返回值则传递给 p 的回调函数
Promise.allSettled()
- Promise.allSettled()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例
- 只有等到所有这些参数实例都返回结果,不管是 fulfilled 还是 rejected ,包装实例才会结束
- Promise.allSettled()方法返回一个在所有给定的promise都已经fulfilled或rejected后的promise,并带有一个对象数组,每个对象表示对应的promise结果。
- 当您有多个彼此不依赖的异步任务成功完成时,或者您总是想知道每个promise的结果时通常使用它。
相比之下,Promise.all()更适合彼此相互依赖或者在其中任何一个reject时立即结束。
ini
const promises = [
fetch('/api-1'),
fetch('/api-2'),
fetch('/api-3'),
];
await Promise.allSettled(promises);
removeLoadingIndicator();
使用场景
链式调用
javascript
const p=new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('x')
},1000)
}).then((res)=>{
console.log("res1",res);
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(res+'x')
},1000)
})
}).then((res)=>{
console.log("res2",res);
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(res+'x')
},1000)
})
}).then((res)=>{
console.log("res3",res);
})
使用Promise+Ajax方式实现多次请求
传统jQuary:
javascript
$.ajax({
url:"../data/student.json",
// type: ,
// data: ,
success:function (resultData) {
console.log("The 1st request,and the student's basic information is :",resultData);
//从第二次异步请求开始,已经出现了"ajax嵌套"
$.ajax({
url:`../data/student_detail_${resultData.id}.json`,
success(resultData) {
console.log("The 2nd request,and the student's detailed information is :",resultData)
},
error(errInfo) {
console.log("Oh oh,Something bad happened:",errInfo);
}
})
},
error(errInfo) { //ES6新特性------------对象的方法简写。
console.log("Something bad happened:",errInfo);
}
})
使用ES6新特性Promise:
typescript
let promise = new Promise((resolve,reject) => {
//发出Ajax异步请求
$.ajax({
url:"../data/student.json",
// type:
// data:
// dataType:
success(resultData) {
console.log("Promise's 1st request,and the student's basic information is :",resultData);
//该方法将resultData参数传递到then()
resolve(resultData);
},
error(errInfo) {
// console.log("Promise's 1st request---Perhaps sth bad happened:",errInfo);
//使用ES6---Promise提供的catch机制来统一处理异常
reject(errInfo);
}
})
})
//2.第二次请求在这里执行!
promise.then(resultData => { //箭头函数形参的简写
// 调用链, 通过return将Promise对象返回给下一个调用者。
return new Promise((resolve, reject) => {
$.ajax({
//url采用了模板字符串的形式
url:`../data/student_detail_${resultData.id}.json`,
success(resultData) {
console.log("Promise's 2nd request,and the student's detailed information is :",resultData);
/*
可以在第二次请求的success回调函数里,
继续使用resolve(resultData);发出第三次请求。
*/
resolve(resultData);
},
error(errInfo) {
// console.log("Promise's 2nd request---Perhaps sth bad happened:",errInfo);
reject(errInfo);
}
})
})
}).then(resultData => {
/*
(1)此处输出的resultData,来自第二次Ajax请求的success中的"resolve(resultData);"
(2)可以在这个then方法中,继续通过
"return new Promise((resolve, reject) => { $.ajax({}) })"
的方式来发出第三次Ajax异步请求。
(3)第三次Ajax异步请求,是基于第二次Ajax请求获取的数据resultData。
*/
console.log("After 2nd request, THE resultData =",resultData);
}).catch(errInfo => {
console.log("U~ Promise's request---Perhaps sth bad happened:",errInfo);
})
代码优化:
javascript
function getRequest(url, data) {
return new Promise((resolve, reject) => {
$.ajax({
url:url,
data:data,
success(resultData) {
resolve(resultData);
},
error(err) {
reject(err);
}
})
})
}
//1.第一次请求 --- 得到student.json中保存的数据
getRequest("../data/student.json").then(resultData => {
console.log("student =", resultData);
//2.第二次请求 --- 得到student_detail_1.json中保存的数据
return getRequest(`../data/student_detail_${resultData.id}.json`).then(resultData => {
console.log("student_1 =", resultData);
//3.第三次请求 --- 得到custodian_1.json中保存的数据
return getRequest(`../data/custodian_${resultData.id}.json`).then(resultData => {
console.log("custodian_1 =",resultData);
})
})
}).catch(err => {
console.log("Perhaps something bad happened:",err);
})