Promise开发【进阶】
目录
前言
Promise是什么
Promise的三种状态
pending
fulfiiled
rejected
Promise的异步操作
then()
catch()
finally()
Promise的链式调用
链式Promise状态确定
链式Promise返回值
Promise值穿透
then()方法的值穿透
catch()方法的值穿透
finally方法的值穿透
总结
Promise.race
Promise错误捕获的边界
catch只能捕获其前面的错误
catch捕获规则
其它
前言
本文对于Promise基础部分有简单解释,但更多的是尝试解释Promise的一些进阶知识
Promise是什么
Promise是JavaScript中处理异步操作的一种解决方案,它是一个对象,表示一个异步操作的最终完成或失败
Promise就像一个“承诺”:
- 我给你一个承诺,我会在未来某个时间完成某个任务,但是什么时候完成我不能保证
- 这个任务可能成功也可能失败
- 通过.then()获取成功的结果
- 通过.catch()获取失败的结果
Promise的三种状态
Promise有三种状态:
- pending(等待中):初始状态,既不是成功,也不是失败
- fulfiied(已成功):操作成功
- rejected(已失败):操作失败
Promise的状态切换只有两条路径:
- pending -> fulfilled
- pending -> rejected
Promise的状态并不会根据操作的成功与否自动切换,状态的切换由您定义
如果您想,您甚至可以让操作出错时返回一个Promise<fulfilled>状态
(如果您的期望就是Promise出错,那确实是fulfilled状态)
pending
当创建一个Promise后,它立马就进入“pending”状态,并且尝试执行Promise中的操作
const promise = new Promise((resolve, reject) => {
})
fulfiiled
让Promise进入fulfilled状态,只需调用Promise给定的resolve()方法即可进入fulfilled状态
// pending -> fulfiiled
const promise = new Promise((resolve, reject) => {resolve();
})
console.log(promise); // Promise { undefined }
如果在promise内部重新创建一个promise2对象,让它调用自身的resolve方法,promise能成功进入fulfilled状态吗?
// pending -> fulfiiled
const promise = new Promise((resolve, reject) => {const promise2 = new Promise((resolve, reject) => {resolve("成功进入fufilled状态");})console.log("promise2:",promise2);
})
console.log("promise:",promise);
结果:
promise2成功进入fulfiiled状态,但是promise仍然卡在pending状态,这说明promise的状态只受自身resolve()的控制
rejected
让Promise进入reject状态,只需调用Promise给定的reject()方法即可进入rejected状态
// pending -> rejected
const promise = new Promise((resolve, reject) => {reject("进入rejected状态");
})
console.log(promise);
同样,如果在promise内部创建一个promise2对象,让它执行自身的reject(),对于promise是无影响的
Promise的异步操作
Promise的fulfiiled状态和rejected状态都是可以被捕获的,使用then()和catch(),不过这两个方法都是“微任务”,而Promise里的代码(执行器)属于同步代码,所以在使用时要注意调用时机
(不了解微任务是什么?欢迎参考我的文章:Vue中nextTick()的理解_vue nexttick-CSDN博客)
then()
then()方法用来捕获promise对象的fulfilled状态
语法:
// pending -> fulfilled
new Promise((resolve, reject) => resolve("进入fulfilled状态")).then(value => {console.log(value);},error => {console.log(error);})
- value => {}:value是resolve(param)里的param,用于捕获fulfilled状态
- error => {}:error只捕获当前Promise的rejected状态,使用较少
catch()
catch()方法用来捕获promise对象的rejected状态
语法:
// pending -> rejected
const promise = new Promise((resolve, reject) => {reject("失败了");
}).catch((error) => {console.log(error);
})
- error:接收reject()传递的错误参数
finally()
finally()方法不管Promise状态是fulfilled或rejected状态,都会执行,它不接接收参数,finally是微任务
const promise = new Promise((resolve, reject) => {resolve('成功');
}).then((value) => {console.log(value);throw new Error('失败');
}).catch((error) => {console.log(error);
})
效果:
Promise的链式调用
Promise的三种回调:“then()”、“catch()”、“finally()”都会返回一个新的Promise对象,这意味着我们可以无限嵌套调用三种回调方法,也就是传统意义上的链式调用。这也意味着,如果你想,你可以任意搭配三种回调的顺序(你甚至可以写出反人类的代码)
// then -> catch -> finally(推荐顺序)
const promise = new Promise((resolve, reject) => {resolve("成功!");
}).then((value) => {console.log(value);return value + "then";
}).catch((error) => {console.log(error);return error + "catch";
}).finally(() => {console.log("finally")
})
// finally -> then -> catch(反人类代码片段1)
const promise2 = new Promise((resolve, reject) => {reject("错误!");
}).finally(() => {console.log("finally");return "finally";
}).then((value) => {console.log(value);return value + "then";
}).catch((error) => {console.log(error);return error + "catch";
})
// then -> finally -> catch(反人类代码片段2)
const promise3 = new Promise((resolve, reject) => {reject("错误!");
}).then((value) => {console.log(value);return "then";
}).finally((value) => {console.log(value);return "finally";
}).catch((error) => {console.log(error);return error + "catch";
})
注意:
- 因为finally不能接收参数,所以finally在链式调用中不宜放在中间位置,因为这会导致finally之前的Promise传递的参数无法正确传递给finally之后的Promise
- catch会捕捉该位置之前的所有回调的错误
链式Promise状态确定
因为then()、catch()、finally()都会返回一个新的Promise对象,那么这些Promise对象的状态如何确定呢?
then()和catch()的回调执行结果直接决定新的Promise状态,即它们的返回值决定了新的Promise状态
- 如果返回的是非Promise值(number、string、object、bool),新Promise状态都为fulfilled
- 如果返回的是另一个Promise对象,新Promise会“继承”这个Promise的状态和结果
- 回调函数抛出错误(throw),新Promise状态为rejected
- 回调函数没有返回值(默认返回undefined),新Promise状态为fulfilled
// 返回非Promise值,新Promise状态为fulfilled
const promise1 = new Promise((resolve) => {resolve('成功');
}).then(() => {console.log("promise1:第一个then,我返回了一个布尔值");return false;
}).then((value) => {console.log("promise1:上一个promise是fulfilled",value);
}).catch((error) => {console.log("promise1:上一个promise是rejected",error);
})
// 第一个then,我返回了一个布尔值
// 上一个promise是fulfilled false// 返回Promise对象,新Promise状态由返回的Promise对象决定
const promise2 = new Promise((resolve) => {resolve('成功');
}).then(() => {console.log("promise2:第一个then,我返回了一个Promise对象");return new Promise((resolve, reject) => {reject('失败');});
}).then((value) => {console.log("promise2:上一个promise是fulfilled",value);
}).catch((error) => {console.log("promise2:上一个promise是rejected",error);
})
// 第一个then,我返回了一个布尔值
// 上一个promise是rejected 失败// 抛出错误,新Promise状态为rejected
const promise3 = new Promise((resolve) => {resolve('成功');
}).then(() => {console.log("promise3:第一个then,我抛出一个错误");throw new Error('错误');
}).then((value) => {console.log("promise3:上一个promise是fulfilled",value);
}).catch((error) => {console.log("promise3:上一个promise是rejected",error);
})
// 第一个then,我抛出一个错误
// 上一个promise是rejected Error: 错误 at C:\Users\Administrator\Desktop\typescriptStudy\promise.js:36:11
效果:
链式Promise返回值
在then()和catch()中,return是可选的:
- 若需要将当前回调的结果传递给下一个链式调用,必须用return(否则下一个回调接受逇是undefined)
- 若不需要传递给下一个Promise,可以省略return(默认返回undefined)
finally()中,return是无意义的
无论finally的回调是否return值,都不会影响下一个链式调用接收的结果,因此通常不需要再finally中使用return
then、catch、finally无法接收到finally的return值:
const promise = new Promise((resolve, reject) => {resolve('成功!');
}).then((value) => {console.log("then成功",value);
}).finally(() => {console.log("finally");return "finally返回值"
}).then((value) => {console.log("then成功2",value);
})
效果:
Promise值穿透
当then()或catch没有传入对应的回调函数时,Promise会将上一个环节的结果原封不动的传递给下一个对应的回调
then()方法的值穿透
当then()没有传入第一个参数(fulfilled回调)时,会自动将上一个Promise的成功结果传递给下一个then()的fulfilled回调
Promise.resolve(100).then() // 没有传入回调函数,因此值穿透.then() // 同样没有传入回调函数,继续值穿透.then((value) => {console.log(value); // 输出:100})
catch()方法的值穿透
当catch()没有传入回调函数时,也会和then()一样将上一个Promise的结果传递给下一个rejected回调
Promise.reject('错误').catch().catch().catch((error) => {console.log(error);})
finally方法的值穿透
finally本身不接收参数,但它仍然可以值穿透
Promise.resolve("成功").finally().then((value) => {console.log(value);})
总结
值穿透不会改变Promise的状态,只是“原样传递”:
- 上一个是fulfiiled状态,穿透后下一个依然是fulfilled
- 上一个是rejected状态,穿透后下一个依然是rejected
Promise.race
Promise.race接收一个Promise数组,只要有一个Promise的状态改变,race就会捕获并返回该Promise
// Promise.race()
const p1 = new Promise((resolve) => {setTimeout(() => {resolve(1);},3000)
})
const p2 = new Promise((resolve) => {setTimeout(() => {resolve(2);},1000)
})
Promise.race([p1, p2]).then((value) => {console.log(value);
})
根据这个特性,我们可以很方便的实现一个超时器,当某任务超时,执行超时器函数。
// Promise实现超时器
// 模拟一个任务,它执行需要3s
const targetTask = new Promise((resolve, reject) => {setTimeout(() => {resolve("任务完成");},3000)
})
// 封装超时器容器
const timeoutFunction = (task,delay) => {// 超时器函数const timeout = (delay) => {return new Promise((resolve, reject) => {setTimeout(() => {reject("任务超时");},delay)})}return Promise.race([task, timeout(delay)]);
}
// 调用超时器容器
timeoutFunction(targetTask,2000).then((value) => {console.log(value);}).catch((error) => {console.log(error);})
Promise错误捕获的边界
catch只能捕获其前面的错误
当一个错误已经被前面的catch捕获时,该错误就不会被当前的catch捕获了
Promise.reject("error1").then(() => {console.log("我不会被执行");}).catch((error) => {// 捕获error1console.log("error:", error);throw "error2"}).then(() => {console.log("我不会被执行");}).catch((error) => {// 捕获error2console.log("error:", error);});
catch捕获规则
catch可以捕获同步错误(throw、回调执行出错)和异步操作中通过reject抛出的错误
但是无法直接捕获Promise执行器或回调中异步代码的同步错误
new Promise((resolve, reject) => {setTimeout(() => {console.log('我不会被捕获');throw "Promise构造器内的错误"},0);
}).catch((error) => {console.log('error:',error); // 无效,无法被捕获
})new Promise((resolve, reject) => {setTimeout(() => {console.log('我会被捕获');reject("Promise构造器内的错误")},0);
}).catch((error) => {console.log('error:',error);
})
效果:
第一个Promise错误没有被捕获,从而导致代码被堵塞,这是因为Promise构造器内错误无法被catch()捕获,我们可以手动添加try catch语句,来捕获同步代码错误:
new Promise((resolve, reject) => {setTimeout(() => {try {console.log('我会被捕获');throw "Promise构造器内的错误"}catch(error) {console.log('error:',error);}},0);
}).catch((error) => {console.log('error:',error); // 无效,无法被捕获
})
效果:
其它
更多JavaScript学习可以参考我的专栏:
JavaScript_是洋洋a的博客-CSDN博客