当一个函数作为参数传入另一个函数中,并且它不会立即执行,只有当满足一定条件后该函数才可以执行,这种函数就称为回调函数。回调地狱就是在此基础上代码一层层的嵌套,使代码的可读性非常差。例如:
//回调地狱 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() `