1.什么是异步回调地狱?
当一个函数作为参数传入另一个函数中,并且它不会立即执行,只有当满足一定条件后该函数才可以执行,这种函数就称为回调函数。回调地狱就是在此基础上代码一层层的嵌套,使代码的可读性非常差。例如:
//回调地狱
setTimeout(function () { //第一层
console.log('111');//等3秒打印111在执行下一个回调函数
setTimeout(function () { //第二层
console.log('222');//等2秒打印222在执行下一个回调函数
setTimeout(function () { //第三层
console.log('333');//等一秒打印333
}, 1000)
}, 2000)
}, 3000)
2.解决回调地狱的方法
2.1 生成器(es6中的语法)
- 生成器是一个特殊的函数(在函数名前面加一个*号),用来解决异步编程的回调地狱问题,语法行为和传统函数完全不同
- yield语句:函数代码的分隔符,把函数代码切割成几块通过next()来控制代码的一个向下的执行
- 使用iterator 迭代器对象 说明可以使用for of来遍历 每一次调用返回的结果 是yield后面的内容
- next执行的时候是可以传参的,参数将作为上一个yield语句整体返回结果 看例子:
//生成器函数实例例子
//需求 只有拿到用户信息之后才能拿到用户的订单信息,继而拿到用户订单的商品信息
function one(){
setTimeout(()=>{
let data="用户信息"
iterator.next(data)
},1000)
};
function two(){
setTimeout(()=>{
let data="订单信息"
iterator.next(data)
},1000)
};
function three(){
setTimeout(()=>{
let data="商品信息"
iterator.next(data)
},1000)
};
function * gen(){
let users= yield one();
console.log(users)
let orders= yield two();
console.log(orders)
let goods= yield three();
console.log(goods)
}
let iterator=gen()
iterator.next()//控制台里依次打印出用户信息 订单信息 商品信息
2.2 Promise
es6引入的异步编程的新解决方案,语法上promise是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果
- new Promise()括号里的参数是一个函数类型的值。且函数有两个参数,一个resolve,一个reject 分别代表成功和失败的状态,然后函数里面封装的是一个异步的操作,我们可以在函数里调用reject或者resolve来改变状态(成功或者失败)
- 然后就可以调用.then方法
- then方法也有两个参数,且参数是两个函数类型,每一个函数都有一个形参,成功的形参一般叫value,失败的形参一般叫reason,当我们promise里调用了resolve代表成功了,它就会执行参数是value的这个函数,反之 如果调取失败函数 也是一样的 执行reason方法
- then方法的返回结果是promise对象,对象状态由回调函数的执行结果决定
- 1.如果回调函数中返回的结果是非promise对象的属性,状态为成功,返回值为对象的成功状态 所以then可以链式调用
//按顺序读取文档
const fs=require("fs");
const p=new Promise((resolve,reject) => {
fs.readFile('./book/bookOne.md',(err,data)=>{
resolve(data)
})
})
p.then(value=>{
return new Promise((resolve,reject)=>{
fs.readFile("./book/bookTwo.md",(err,data)=>{
resolve([value,data])
})
})
}).then(value=>{
return new Promise((resolve,reject)=>{
fs.readFile('./book/bookThree.md',(err,data)=>{
value.push(data);
resolve(value)
})
})
})
.then(value=>{ console.log(value.join('\r\n'))})
promise中catch方法
是用来指定promise对象的一个失败的回调
我们知道then方法后面两个参数都是函数,第二个函数的参数reason就是指定失败后的回调,
const p=new Promise((resolve,reject)=>{
setTimeout(()=>{
reject("出错啦")
},2000)
})
p.catch(function(reason){
console.error(reason)
})
2.3 async await
它是es8的新特性
async和await结合可以让异步代码像同步代码一样
- async函数的返回值是一个promise对象
- promise对象的结果由async函数执行的返回值决定
await表达式
- 1.await必须写在async函数中
- 2.await右侧的表达式一般为promise对象
- 3.await返回的是promise成功的值
- 4.await的promise失败了,就会抛出异常,需要通过try…catch捕获处理
- 5.返回的结果如果不是一个Promise对象(return 字符串,数字类型等,undefined等),则函数返回的结果就是一个成功的Promise对象
- 6.如果返回的结果是一个promise对象,则成功的promise 的值就是该函数成功的值
// 例子 依次读取文件 1.md 2.md 3.md
const fs=require("fs")
function first(){
return new Promise((resolve,reject)=>{
fs.readFile("./book/1.md",(err,data)=>{
if(err) reject(err);
resolve(data)
})
})
}
function second(){
return new Promise((resolve,reject)=>{
fs.readFile("./book/2.md",(err,data)=>{
if(err) reject(err);
resolve(data)
})
})
}
function three(){
return new Promise((resolve,reject)=>{
fs.readFile("./book/3.md",(err,data)=>{
if(err) reject(err);
resolve(data)
})
})
}
async book(){
let article=await first()
let article2=await second()
let article3=await three()
console.log(article.toString())//我这里文件夹里的是汉字,所以用toString转化一下
console.log(article2.toString())
console.log(article3.toString())
}
book()
你更喜欢哪一种语法呢,小编最喜欢 async await 嘻嘻~