文章目录
我们现在需要多个异步任务必须顺序执行
异步函数:各自执行各自的,互不干扰,互相之间也不会等待。
方法一、回调函数(回调地狱)
i. 前一项任务:
function 前一项任务(购物车){
异步任务
异步任务最后一句话之后
购物车()
}
ii. 调用前一项任务时:
前一项任务(function(){ 下一项任务() })
示例:
// 下一项任务
function erya(next) { //异步
console.log(`erya起跑!`);
setTimeout(() => { //异步:在主程序外执行,主程序不会等它执行完
console.log(`erya到达终点!`);
// 之后,主动调用下一项任务
next();
}, 6000);
}
function feifei(next) { //异步
console.log(`feifei起跑!`);
setTimeout(() => {
console.log(`feifei到达终点!`);
// 之后,主动调用下一项任务
next();
}, 4000);
}
function bear() { //异步
console.log(`bear起跑!`);
setTimeout(() => {
console.log(`bear到达终点!`);
}, 2000);
}
// erya();
// feifei();
// bear();
erya(function() {
yuan(function(){
bear();
});//yuan的next=function(){bear()}
})//erya的next=function(){yuan()}
// 当erya执行完最后一句话,自调用erya的next:function(){yuan()},函数yuan开始执行
// 当yuan执行完最后一句话,自调用yuan的next:function(){bear()},函数bear开始执行
图解
①erya的推车装着yuan,yuan的推车装着bear。
第一段路erya推车跑,跑到终点:
②erya到达终点后,将自己的推车中的yuan和yuan的推车释放。
yuan从第二段路开始跑,跑到终点:
③yuan到达终点后,将自己推车中的bear释放,bear开始跑第三段路,直到终点
问题:回调地狱
按照以上方式,如果要先后执行的任务多了会形成很深的嵌套结构——我们称之回调地狱。
极其不优雅,极其不便于维护
方法二:promise
今后,只要多个异步任务必须顺序执行时,都要用promise技术来代替回调函数方式
步骤:
1). 定义前一项任务时:
调用return new Promise()
所在的异步任务函数,既可以执行异步函数的任务,又可以返回格子间
function 前一项任务(){
return new Promise(
// 赠
function(开关){
原异步任务
异步任务最后一句话
调用开关()//->通.then(),自动执行.then中的下一项任务
}
)
}
2). 连接前后两个异步任务:
可用.then
与下一项任务串联
前一项任务().then( 后一项任务 )
多个异步任务串联:
任务1().then(任务2).then(任务3)
之后的任务一定不要加(),加()表示立刻执行。
而我们不希望后边的任务立刻执行
示例:
//promise
function erya() { //异步
// 用new Promise(function(door)) {……}包裹原来的异步函数代码
// 格子间 门
return new Promise(function(door){
console.log(`erya起跑!`);
setTimeout(() => {
console.log(`erya到达终点!`);
// 在原异步函数的最后一行代码,主动调用door开门
door();//自动执行.then()中串联的下一项任务
}, 6000);
})
}
function yuan() { //异步
return new Promise(function(door){
console.log(`yuan起跑!`);
setTimeout(() => {
console.log(`yuan到达终点!`);
door();
}, 4000);
})
}
function bear() { //异步
console.log(`bear起跑!`);
setTimeout(() => {
console.log(`bear到达终点!`);
}, 2000);
}
// .then之前必须是格子间对象,之后可以是格子间也可以是函数
erya().then(yuan).then(bear);
// 执行erya的任务,执行完,调用door()开门=>自动执行.then()中的yuan
图解:
①new Promise会创建一个关上门的格子间,附赠door(),调用door()可以开门
②且有一个钩子.then
,可以用来串联连接
.then
之前必须是格子间对象,之后可以是格子间也可以是函数
之后的任务一定不要加(),加()表示立刻执行。而我们不希望后边的任务立刻执行
2.1异步任务传参(单个)
利用door()进行传参,door默认只能传一个变量
步骤:
1.前一项任务:
function 前一项任务(){
return new Promise(
function(开关){
var 变量=值
原异步任务
异步任务最后一句话
调用开关( 变量 )//->通.then(),自动执行.then中的下一项任务
}
)
}
2.后一项任务:
function 后一项任务(形参) {
//形参=前一项任务中的变量值
}
示例:
function erya() {
return new Promise(function(door) {
var bang="接力棒";
console.log(`erya拿着 ${bang} 起跑...`);
setTimeout(function() {//异步
console.log(`erya拿着 ${bang} 到达终点!`);
door(bang);//自动执行.then()中串联的下一项任务
}, 6000);
})
}
function yuan(bang2) {//异步
//bang2=erya中door()给的"接力棒"
return new Promise(function(door) {
console.log(`yuan拿着 ${bang2} 起跑...`);
setTimeout(function() {//异步
console.log(`yuan拿着 ${bang2} 到达终点!`);
door(bang2); //通知.then中串联的下一项任务可以开始执行!
}, 4000)
})
}
function bear(bang3) {//异步
//bang3=bear()中door()传来的"接力棒"
console.log(`bear拿着 ${bang3} 起跑...`);
setTimeout(function() {
console.log(`bear拿着 ${bang3} 到达终点!`)
}, 2000)
}
erya().then(yuan).then(bear);
结果显示如图:
图解:
在第一个格子间对象中定义接力棒var bang=接力棒
,然后用door()传递接力棒bang,下一个格子间对象要定义一个形参bang2接收前一个传来的bang……以此类推
2.2异步任务传参(多个)
door中默认只能接收一个变量!
那么如果我们想要传递多个参数呢?
可以放在数组或对象中传递:
步骤:
1.前一项任务将door改为:
door([值1,值2,.…])
或者
door({属性1:值1,属性2:值2….. )
2.后一项任务: 参数解构
function下一项任务([形参1,形参2])
或者
function下一项任务({属性1,属性2})
示例:
function erya() {
return new Promise(function(door) {
var bang="接力棒";
var drink = "水"
console.log(`erya拿着 ${bang} 和${drink}起跑...`);
setTimeout(function() {//异步
console.log(`erya拿着 ${bang}和 ${drink}到达终点!`);
door({bang, drink});//传入接力棒
}, 6000);
})
}
function yuan({bang, drink}) {//异步
//bang2=erya中door()给的"接力棒"
return new Promise(function(door) {
console.log(`yuan拿着 ${bang} 和${drink}起跑...`);
setTimeout(function() {//异步
console.log(`yuan拿着 ${bang} 和${drink}到达终点!`);
door({bang, drink}); //通知.then中串联的下一项任务可以开始执行!
}, 4000)
})
}
function bear({bang, drink}) {//异步
//bang3=bear()中door()传来的"接力棒"
console.log(`bear拿着 ${bang} 和${drink} 起跑...`);
setTimeout(function() {
console.log(`bear拿着 ${bang} 和${drink}到达终点!`)
}, 2000)
}
erya().then(yuan).then(bear);
2.3 错误处理
new Promise赠送了两个门: .
a. door,通往正常路线下一个.then
的门
b. err,通往出错路线下一个.catch
的门
1). 前一项任务:
function 前一项任务() {
return new Promise(
// 赠
function(成功的开关, 失败的开关) {
var 变量 = 值
原异步任务
异步任务最后一句话
如果异步任务执行成功
调用成功的开关( 变量 )//->通.then(),自动执行.then中的下一项任务
否则如果一部任务执行失败
调用失败的开关(错误提示信息)//->通最后的.catch(),后续.then()不再执行。
}
)
}
2). 调用时:
前一项任务().then(下一项任务).then(...)
.catch(function(错误提示信息){
错误处理代码
})
示例:
function erya() {
// 赠两个门 正确 出错
return new Promise(function(door,err) {
var bang="接力棒";
console.log(`erya拿着 ${bang} 起跑...`);
setTimeout(function() {//异步
//用随机数模拟出错的概率
var r=Math.random();//0~1
//假设r<0.6 60% 可以正常执行
//如果没有摔倒
if (r < 0.6) {
console.log(`erya拿着 ${bang} 到达终点!`);
//之后,主动调用door开门!
door(bang);//自动执行.then()中串联的下一项任务
} else {//否则如果摔倒了r>0.4 40%
//走错误处理的门
err("erya摔倒了!退赛!")
}
}, 6000)
})
}
function yuan(bang2) {//异步
//bang2=erya中door()给的"接力棒"
return new Promise(function(door, err) {
console.log(`yuan拿着 ${bang2} 起跑...`);
setTimeout(function() {
var r = Math.random();
if (r<0.6) {
console.log(`yuan拿着 ${bang2} 到达终点!`);
door(bang2); //通知.then中串联的下一项任务可以开始执行!
} else {
//走错误处理的门
err("yuan摔倒了!退赛!")
}
}, 4000)
})
}
function bear(bang3){
//bang3=yuan()中door()传来的"接力棒"
console.log(`bear拿着 ${bang3} 起跑...`);
setTimeout(function() {
console.log(`bear拿着 ${bang3} 到达终点!`)
}, 2000)
}
erya().then(yuan).then(bear).catch(function(errMsg){
console.error(errMsg);
})
2.4 Promiss对象三大状态: (学名)
-
当异步任务执行过程中,整个
new Promise()
对象处于 pending(挂起) 状态 -
当异步任务成功执行完,调用成功的开关函数时,整个
new Promise()
对象切换为 fulfilled(成功) 状态,new Promise()会自动调用.then()
执行下一项任务 -
当异步任务执行出错,调用失败的开关函数时,整个
new Promise()
对象切换为 rejected(出错) 状态,new Promise()会自动调用.catch()
执行错误处理代码
行业内:
1). 正确的开关: resolve 同意继续
2). 失败的开关: reject 拒绝继续
2.5 Promise.all()
const p=new Promise([p1,p2,p3])
p的状态由p1、p2、p3决定,分成两种情况:
(1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组(按数组顺序),传递给p的回调函数。
(2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
1. all后的数组中每个格子间的异步任务都是并发执行的,谁也不等谁
2. all后的.then()
注定会等最慢的一个格子间执行完,才自动执行
function erya() {
return new Promise(function(resolve){
console.log(`erya起跑!`);
setTimeout(() => {
console.log(`erya到达终点!`);
resolve();
}, 6000);
})
}
function yuan() { //异步
return new Promise(function(resolve){
console.log(`yuan起跑!`);
setTimeout(() => {
console.log(`yuan到达终点!`);
resolve();
}, 4000);
})
}
function bear() { //异步
return new Promise(function(resolve){
console.log(`bear起跑!`);
setTimeout(() => {
console.log(`bear到达终点!`);
resolve();
}, 2000);
})
}
//希望三个人百米赛跑(谁也不等谁),但是最后一个人跑完时,才输出比赛结束!
Promise.all([
//只有调用异步函数,才能返回new Promise()格子间
erya(),//return new Promise()
//resolve()
yuan(),//return new Promise()
//resolve()
bear()//return new Promise()
//resolve()
]).then( function() {
console.log("比赛结束!");
})
若传参,返回的数组:
顺序是按照all中数组的顺序
2.6 Promise.race()
Promise.race():
const p = Promise.race([p1, p2, p3]);
p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。
那个率先改变的Promise实例的返回值,就传递给p的回调函数。
Promise对象的特点
(1)对象状态不受外界干扰,Promise代表一个异步操作
(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果
判断题:对于一个向后台获取数据已经产生结果的promise:p1,再次调用p1.then,不会去重新发起请求获取数据 √