多层回调函数的相互嵌套,就形成了回调地狱
回调地狱的缺点:
代码耦合性太强,牵一发而动全身,难以维护
大量冗余的代码相互嵌套,代码的可读性变差
setTimeout(()=>{//第一层回调函数
setTimeout(()=>{//第二层回调函数
setTimeout(()=>{第三层回调函数
},3000)
},7000)
},1000)
如何解决回调地狱的问题
为了解决回调地狱的问题,ES6中新增了promise的概念
Promise的基本概念
promise 是一个构造函数
我们可以创建promise 的实例 const p = new Promise()
new 出来的Promise 实例对象,代表一个异步操作
Promise.prototype 上包含一个 .then()方法
每一次 new Promise() 构造函数得到的实例对象
都可以通过原型链的方式访问到.then() 方法,例如 p.then()
.then() 方法用来预先 指定成功和失败的回调函数
p.then(成功的回调函数,失败的回调函数)
p.then(result=>{},error=>{})
调用.then() 方法时,成功的回调函数是必选的,失败的回调函数是可选的
then-fs 的基本使用
由于node.js官方提供的fs模块仅支持以回调函数的方式读取文件,不支持Promise的调用方式。因此,需要先运行如下的命令,安装then-fs这个第三方包,从而支持我们基于Promise的方式读取文件的内容:
npm install then-fs
调用then-fs 提供的readFile() 方法,可以异步地读取文件的内容,它的返回值Promise的实例对象。因此可以调用 .then()方法为每个Promise异步操作指定成功和失败之后的回调函数。
/***
* 基于Promise 的方式读取文件
*/
import thenFs from 'then-fs'
//注意 .then() 中的失败回调是可选的,可以被省略
thenFs.readFile('./files/1.txt','utf8').then(r1=>{console.log(r1)},err1=>{})
thenFs.readFile('./files/1.txt','utf8').then(r1=>{console.log(r1)},err1=>{})
thenFs.readFile('./files/1.txt','utf8').then(r1=>{console.log(r1)},err1=>{})
//注意:上述的代码无法保证文件的读取顺序,需要做进一步的改进!
.then的特性:如果上一个.then()方法中返回一个新的Promise实例对象,则可以通过下一个.then()继续进行处理。通过.then()方法的链式调用,就解决了回调地狱的问题
Ptomise支持链式调用,从而来解决回调地狱的问题。代码如下:
在promise的链式操作中如果发生了错误,可以使用promise.prototypy.catch方法进行捕获和处理
thenFs.readFile('./files/1.txt','utf8')
.catch(err=>{ //放到前面 可以捕获第一行的错误,并输出错误信息
console.log(err.message)// 由于错误已被处理,所以并不影响后续.then 的正常执行
})
.then(r1=>{ // Ptomise支持链式调用
console.log(r1)
rutern thenFs.readFile('./files/2.txt','utf8')
})
.then(r2=>{ // Ptomise支持链式调用
console.log(r2)
rutern thenFs.readFile('./files/3.txt','utf8')
})
.then(r3=>{ // Ptomise支持链式调用
console.log(r3)
})
.catch(err=>{ //放到最后可以捕获前面发生的所有错误,并输出错误的信息
console.log(err.message)
})
Promise.all方法
Promise.all() 方法会发起并行的Promise异步操作,等所有的异步操作全部结束后才会执行下一步的.then 操作(实现一个等待机制)
1.定义一个数组,存放3个读取文件的异步操作
const promiseArr=[
thenFs.readFile('./files/1.txt','utf8'),
thenFs.readFile('./files/2.txt','utf8'),
thenFs.readFile('./files/3.txt','utf8'),
]
2.将Promise 的数组,作为Promise.all() 的参数
Promise.all(promiseArr)
//所有文件读取成功(等待机制) 数组中的文件读取顺序就是.then中结果返回的顺序
.then( result=>{
console.log(r1,r2,r3)
})
.catch(err=>{ //捕获promise 异步操作中的错误
console.log(err.message
})
Promise.race()方法
Promise.race()方法会发起并行的Promise异步操作,只要任何一个异步操作完成,就立即执行下一步的.then 操作(赛跑机制)。
1.定义一个数组,存放3个读取文件的异步操作
const promiseArr=[
thenFs.readFile('./files/1.txt','utf8'),
thenFs.readFile('./files/2.txt','utf8'),
thenFs.readFile('./files/3.txt','utf8'),
]
2.将Promise 的数组,作为Promise.all() 的参数
Promise.race(promiseArr)
//数组中执行最快的那一个异步操作的值
.then( result=>{
console.log(r1,r2,r3)
})
.catch(err=>{ //捕获promise 异步操作中的错误
console.log(err.message
})
基于Promise 封装读文件的方法
方法的封装要求:
方法的名称要定义为getFile
方法接受一个形参fpath,表示读取的文件路径
方法的返回值为一个Promise 实例对象
创建具体的异步操作
如果想要创建具体的异步操作,则需要在new Promise() 构造函数期间,传递一个function函数,将具体的异步操作定义到function函数内部
//1.方法的名称为 getFile 读文件的异步操作
//2.方法接受一个形参fpath 表示要读取的文件的路径
function getFile(fpath){
//3.方法的返回值为Promise 的实例对象
return new Promise(function (resolve,reject ){
//4.下面这行代码,表示这是一个具体的、读文件的异步操作
fs.readFile(fpath,'utf8',(err,dataStr)={
//返回值
if(err) return reject(err)
resolve(dataStr)
})
})
}
//getFile 方法的调用过程
getFile('读取文件的路径').then(成功的回调,失败的回调)
在请求一个接口拿到一个数组,使用该数组id循环去请求一个接口拿到数据时执行的操作:
//先定义一个空数组
let spreadList = []
//A接口,这里简单写一下,部分参数省略了
uni.request({
url: A,
success: (res) => {
//res.list为请求成功后返回的数组,这里拿数组里每个对象的id字段请求B接口
res.list.forEach(item => {
spreadList.push(
new Promise((resolve, reject) => {
//请求B接口
uni.request({
url: B,
data: {
id: item.id
},
success(response => {
//responseB接口返回的一个对象
item.obj = response
resolve(item)
})
})
})
)
})
Promise.all(spreadList).then(res => {
//这里的list则是页面渲染的内容
this.list = res
})
}
})