您的位置:首页 > 大数据 > 人工智能

利用ES6中Promise的用法及 ES7中的async await解决异步函数顺序执行的回调地狱问题

2020-04-20 19:31 946 查看

Promise

1.什么是promise
promise是专门保证多个异步函数,可以顺序执行的机制。而且还防止了回调地狱问题。

2.何时需要使用promise
多个异步调用的函数,要求必须顺序执行.

3.为什么要使用promise
其实用回调函数,也可以实现多个异步函数,顺序执行。但是,使用回调函数,会有回调地狱问题!

需求:多个异步函数需要按顺序执行
-----错误的解决: 仅按顺序调用
---- 结果: 无法保证顺序执行
----因为: 多个异步函数,每个人一条路,互相之间不会等待。

传统的解决: 使用回调函数:

/*1. 在定义函数时,定义一个callback形参变量
在函数内部,最后一句话执行之后,自动调用callback()*/
function zz(callback){//异步!
console.log("开始");
setTimeout(function(){//异步!
console.log("结束")
//等最后一句话执行完,自动调用提前托付的任务callback
callback();
},4000)
}
/*2. 在调用函数时,传入一个函数,函数中包含下一步要执行的操作。——提前托付*/
zz(
//callback
function(){
console.log("比赛结束!")
}
//当zz最后一句话执行完,自动执行callback(),从而完成连续调用
)
//当需要进行连续的异步函数调用时,进行多次callback调用,当重复调用的函数多了便会形成回调地狱

解决方法:使用es6中的promise

前提: 不要用回调函数参数了!
因为回调函数嵌套传参的写法是造成回调地狱的根源
第一步: 在异步函数内部,用new Promise(function(door){ ... })来包裹原函数中所有代码。
其中door是用来打开通向下一项任务的大门的钥匙。
第二步: 在异步任务执行完最后一步之后,调用附赠的开关door(),开门,通知下一项任务可以开始执行。
问题: 不能在函数内写死.then(下一个函数),应该让当前函数可以和之后任意函数自由组合。
第三步: 将整个new Promise()对象返回到函数外部,再用.then()接下一项任务函数
调用支持promise的函数:
前一个异步函数().then(下一个异步函数)
强调: .then()中"下一个异步函数"后不要加(),因为下一个异步函数不是立刻调用,而是在这里等待前一个函数开门。
信任: 前一个函数内调用door()后,就等于通知了.then()中的下一个函数可以开始执行。于是.then()就自动执行下一个函数。
多个异步函数顺序执行:
.then()能否接下一个.then(),取决于前一个.then()中的函数是否也支持promise。如果前一个.then()中的函数支持promise,则.then()可以继续.then()。如果前一个.then()中的函数不支持promise,则不能继续.then()

前后两个函数间传参:
2步:
1. 上一个函数中door(参数值)
2. 下一个函数定义时就要定义一个形参准备接
原理: 当上一个函数调用door(参数值)时,参数值,会顺着.then()交给.then()中的下一个函数的形参变量。在下一个中就可通过自己的形参变量获得上一步传下来的参数值。
局限: door()中只能传一个变量
如果必须穿多个值,则可以将多个值放在数组或对象中整体传入。
错误处理:
如何: 任何一个支持promise的函数中都有另一扇门。如果当前异步任务执行过程中发生错误,就可从另一扇门出来。一旦从报错这扇门出来后,后续.then()都不再执行。
其实new Promise()除了then外,还有另一个方法.catch()。凡是从出错的门出来的代码都进入.catch()中执行错误处理操作(无论前面有多少,catch只用写一次)。
function a(){
//1. 用new Promise将整段代码包裹起来
//3. 将整个new Promise对象抛出到函数外部
return new Promise(
function(door,err){
var bang="a的接力棒";
console.log(`a拿着${bang} 起跑...`);
setTimeout(function(){
if(Math.random()<0.6){
console.log(`a拿着${bang} 到达终点!`);
//2. 在异步函数最后一句话执行完,开门通知下一个人可以开始执行。
console.log(`a开门!把${bang}交给下一个人`)
door(bang);
}else{
console.log(`呀!a摔倒了!!!`);
err("a摔倒了");
}
},6000)
}
)
}

function b(bang){
//1. 用new Promise()包括原函数所有代码
//3. 将new Promise对象返回到函数外部
return new Promise(
function(door,err){
console.log(`b 拿着${bang} 起跑...`);
setTimeout(function(){
if(Math.random()<0.6){
console.log(`b 拿着${bang} 到达终点!`);
//2. 在当前异步任务结束后,自动调用开关door()开门通知下一个函数可以开始执行
console.log(`b开门!把${bang}交给下一个人`)
door(bang)
}else{
console.log(`呀!b摔倒了!!!`);
err("b摔倒了");
}
},2000)
}
)
}

function c(bang){
//1. 用new Promise()包裹原函数左右代码
//3. 返回new Promise()对象到函数外部
return new Promise(
function(door){
console.log(`c 拿着${bang} 起跑...`);
setTimeout(function(){//异步!
console.log(`c 拿着${bang} 到达终点!`)
//2. 当c的最后一步任务执行完,自动开门,通知下一个函数可以开始执行
console.log("c开门!")
door();
},4000)
}
)
}

a()
.then(b)
.then(c)
.then(()=>console.log("比赛结束"))
.catch(function(err){
console.log(err);
console.log("弃权!")
})
/*
a()
//2件事:
//1. 执行a的任务内容: a起跑...
//2. 创建new Promise对象并返回
//return new Promise()
.then(ran)
//能否继续接.then()取决于ran是否支持new Promise()
//当a()中调用door()时,.then()中的ran()开始自动执行!
//也干了2件事:
//1. 调用b中的任务内容: b起跑...
//2. 返回新的new Promise()对象
//return new Promise()
.then(c)
//c是否可接.then()取决于c内部是否返回new Promise()对象
//当b()中调用door()开门时,c()的内容开始执行:
//2件事:
//1. 执行c()的函数任务: c起跑..
//2. 返回新的new Promise()对象
//正是因为dong()返回了新的new Promise对象,才可接下一个.then(函数)
//return new Promise()
.then(function(){
console.log("比赛结束");
})
*/
等待多个异步任务完成才执行(同时进行):
Promise.all([
多个支持promise的函数调用(),
...,
...,
]).then(function(){ 后续操作...})
问题:如果每个异步任务都返回一个接力棒,则如何获得所有接力棒呢?
解决: .then(function(arr){ ... })
其中: arr数组中保存了Promise.all中所有异步函数通过door()返回的执行结果。
强调: arr中返回值存储的顺序和异步函数执行完成的顺序无关。只和调用的顺序有关!
function a(){
return new Promise(
function(door){
var a="a的接力棒";
console.log(`a起跑...`);
setTimeout(function(){
console.log(`a到达终点!`);
console.log(`a开门`)
door(bang);
},6000)
}
)
}

function b(){
return new Promise(
function(door){
var bang="b的接力棒";
console.log(`b起跑...`);
setTimeout(function(){
console.log(`b到达终点!`);
console.log(`b开门!`)
door(bang)
},2000)
}
)
}

function c(){
return new Promise(
function(door){
var bang="c的接力棒";
console.log(`c起跑...`);
setTimeout(function(){
console.log(`c到达终点!`)
console.log("c开门!")
door(bang);
},4000)
}
)
}

Promise.all([//数组中要求必须传入new Promise对象
a(),//new Promise对象
b(),//new Promise对象
c()//new Promise对象
])
.then(arr=>{
console.log("比赛结束");
console.log(`收到以下接力棒:[${arr}]`)
})
Promise的问题: 并没有彻底消灭嵌套:
比如: .then(dong)
.then(function(){
console.log("比赛结束")
}).catch(function(err){
错误处理
})

ES7: async await

解决: ES7: async   await  可按照传统的同步指定的代码一样,编写异步代码顺序执行
只要多个异步任务需要顺序执行:
(async function(){
同步代码;
var 返回值=await 异步函数()
同步代码;
})();
其中: await可让整段匿名函数自调暂时挂起,等待当前异步函数执行完,在执行后续代码!
强调: es7的async和await仅仅简化的是promise函数调用的部分。而并没有简化Promise函数的定义。且,如果想用await,则异步函数必须定义为支持promise的样式。
错误处理: 如果await修饰的异步函数中调用了err()方法,打开了错误的门,则await会认为是程序错误。
应该用try{}catch(err){}来解决
强调: 只有async下的try catch才能捕获异步任务中的错误。没在async下的js基础中所学的try catch是不能捕获异步任务的!因为普通js基础中的try catch属于主程序,不会等待异步任务执行,就已经结束了。即使异步任务出错,try catch因为早就结束了,所以根本捕获不到。
为什么await配合try catch就可以捕获异步任务中的错误呢?
因为await可留住当前程序中的一切代码,等待异步函数执行完。try catch就有可能捕获到异步任务中的错误。
function a(){
return new Promise(
function(door,err){
var bang="a的接力棒";
console.log(`a 拿着${bang} 起跑...`);
setTimeout(function(){
if(Math.random()<0.6){
console.log(`a 拿着${bang} 到达终点!`);
//2. 在异步函数最后一句话执行完,开门通知下一个人可以开始执行。
console.log(`a开门!把${bang}交给下一个人`)
door(bang);
}else{
console.log(`呀!a摔倒了!!!`);
err("a摔倒了");
}
},6000)
}
)
}

function b(bang){
return new Promise(
function(door,err){
console.log(`b 拿着${bang} 起跑...`);
setTimeout(function(){
if(Math.random()<0.6){
console.log(`b 拿着${bang} 到达终点!`);
//2. 在当前异步任务结束后,自动调用开关door()开门通知下一个函数可以开始执行
console.log(`b开门!把${bang}交给下一个人`)
door(bang)
}else{
console.log(`呀!b摔倒了!!!`);
err("b摔倒了");
}
},2000)
}
)
}

function dong(bang){
return new Promise(
function(door){
console.log(`c 拿着${bang} 起跑...`);
setTimeout(function(){//异步!
console.log(`c 拿着${bang} 到达终点!`)
console.log("c开门!")
door();
},4000)
}
)
}
(async function(){
try{
var bang=await a();
//door就相当于return bang
//await代替了.then()
bang=await b(bang);
await c(bang);
console.log("比赛结束");
}catch(err){//代替了.catch()
console.log(err);
console.log("集体退赛!");
}
})();
  • 点赞
  • 收藏
  • 分享
  • 文章举报
前端~维客 发布了6 篇原创文章 · 获赞 1 · 访问量 91 私信 关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: