什么是回调地狱,如何用Promise解决回调地狱<附:Promise.then的第二个参数和catch的区别>

解决异步回调地狱

同步(单)和异步(多)
  • 同步任务:都在主线程上执行,形成一个主线程执行栈
  • 异步任务:通过回调函数实现,主要有以下三种
    • 普通事件,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

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

会飞的战斗鸡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值