ES6(三)——回调地狱和promise异步任务顺序执行(传参、错误处理)


我们现在需要多个异步任务必须顺序执行

异步函数:各自执行各自的,互不干扰,互相之间也不会等待。

方法一、回调函数(回调地狱)

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对象三大状态: (学名)

  1. 异步任务执行过程中,整个new Promise()对象处于 pending(挂起) 状态

  2. 异步任务成功执行完,调用成功的开关函数时,整个new Promise()对象切换为 fulfilled(成功) 状态,new Promise()会自动调用.then()执行下一项任务

  3. 异步任务执行出错,调用失败的开关函数时,整个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,不会去重新发起请求获取数据

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你脸上有BUG

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值