文章目录
解决异步回调地狱
同步(单)和异步(多)
- 同步任务:都在主线程上执行,形成一个主线程执行栈
- 异步任务:通过回调函数实现,主要有以下三种
- 普通事件,click、resize等
- 资源加载,load、error等
- 定时器,setInterval、setTimeout等
异步任务相关回调函数添加到**任务队列(消息队列)**中
回调地狱
多层回调函数嵌套
先来分析一下,我们为什么会写出这种代码呢,原因是,我们想要控制异步程序的执行顺序,我们想让上一个完成后再进行下边的任务,这种代码是很难维护的
为了解决这种情况,es6出现了Promise
先将解决方法写在这里
function func(){
return new Promise((resolve)=>{
setTimeout(() => {
resolve('success')
console.log(`延时1s后输出`);
},1000);
})
.then(()=>{
return new Promise((resolve)=>{
setTimeout(() => {
resolve('success')
console.log(`延时2s后输出`);
},2000);
})
})
.then(()=>{
return new Promise((resolve)=>{
setTimeout(() => {
resolve('success')
console.log(`延时3s后输出`);
},3000);
})
})
}
func()
Promise基本概念
- Promise是一个构造函数
- 可以new实例
- new出来的实例对象,代表一个异步操作
接下来使用b站up主技术蛋老师的灵性图例来解释一下什么是Promise
// new Promise:一个男人和女人相恋了(new 了个承诺)
// resolve:女人怀孕了(成功,解决承诺)
// reject:女人没怀上(失败,拒绝承诺)
// 知道怀没怀上需要时间,就像异步操作
// 之后怀没怀上只有女人才知道
// 如果怀上了,就在resolve后then,女人宣布怀上(成功)
// 没怀上,就在reject后catch,女人宣布没怀上(捕获失败)
// finally:无论怀没怀上,最后都还是结婚了(最终)
// 是否怀孕
const isPregnant = true
const promise = new Promise((resolve,reject)=>{
if(isPregnant){
resolve('怀上')
}else{
reject('没怀上')
}
})
// then里的name是resolve保留的参数
// catch里的name是reject保留的参数
promise
.then(name=>{
console.log(`女人${name}`);
})
.catch(name=>{
console.log(`女人${name}`);
})
.finally(()=>{
console.log('无论如何也要结婚');
})
promise实现图片异步加载
const imgAddress = 'https://himg.bdimg.com/sys/portraitn/item/46ecf7f5'
const imgPromise = (url)=>{
return new Promise((resolve,reject)=>{
const img = new Image()
img.src = url
img.onload = ()=>{
resolve(img)
}
img.onerror = ()=>{
reject(new Error('图片有误'))
}
})
}
imgPromise(imgAddress)
.then(img=>{
document.body.appendChild(img)
})
.catch(err=>{
document.body.innerHTML = err
})
使用Promise时的注意事项
.then之前必须返回一个promise实例
如果想要连着then的话,上一个就必须返回下一个要使用的promise
.then后面要先写一个function(data)用来接收resolve传来的参数
同理.catch后面要先写一个function(data)来接收reject传来的参数
.all()
promise.all()会发起并行的promise异步操作,等所有的异步操作全部结束后才会执行下一步的.then操作
promise.all()返回一个promise实例
const promiseArr = [
thenFs.readFile('./files/1.txt','utf-8')
thenFs.readFile('./files/2.txt','utf-8')
thenFs.readFile('./files/3.txt','utf-8')
]
Promise.all('promiseArr').then(([r1,r2,r3]=>{
console.log(r1,r2,r3)
}))
//并行执行,三个同时读取,且数组中的Promise实例的顺序,就是最终结果的顺序
.race()
赛跑机制与all不同的是race方法只要完成其中任何一个异步操作,就会立即执行下一步的.then操作
使用async和await解决回调地狱
使用promise代码看着还是不是很清爽,我们可以使用async和await解决回调地狱
当然,使用async和await只是取代了.then获取,但是还是需要promise的
// 方法二:async和await
function time1(){
return new Promise((resolve)=>{
setTimeout(()=>{
resolve('success')
console.log('1s后输出');
},1000)
})
}
function time2(){
return new Promise((resolve)=>{
setTimeout(()=>{
resolve('success')
console.log('2s后输出');
},2000)
})
}
function time3(){
return new Promise((resolve)=>{
setTimeout(()=>{
resolve('success')
console.log('3s后输出');
},3000)
})
}
// 使用async修饰函数
async function time(){
// 取代了之前的.then((data)=>{}),直接获取到异步数据
// 代码会按照你写的顺序执行,等待第一个完成再继续第二个
let one = await time1()
console.log(one);// success
let two = await time2()
console.log(two);// success
let three = await time3()
console.log(three);// success
}
time()
补充:Promise的then的第二个参数和catch的区别
.then的第二个参数reject捕获的是前一个promise的错误,而catch捕获的是前一个.then里函数的错误;如果在.then没有第二个参数的时候,catch会因为链式调用,捕获到promise的错误
因此catch的本质是语法糖,实际还是通过then处理
推荐使用catch