Promise对象、同步和异步代码、回调地狱的讲解

简单粗暴一点的说Promise就是一个为了解决异步代码的东西,它可以让代码按照你想要的顺序去执行。

我们先来说说什么是同步代码,什么是异步代码。同步代码就是按顺序执行,如:12345按顺序往下走。只有前一条代码执行完毕之后才会去执行后一条代码。异步代码就是可以不按照顺序执行的代码,如:213465,例如: 网络请求、定时器、文件的读写等均是异步的。我们直接上代码直观的去感受一下

setTimeout(() => {
  
}, 1000)

 ↑这是一个定时器,这是一个可以将包裹在方法体内的代码延迟1000毫秒以后再执行的定时器。

console.log('任务1', moment(new Date()).format('HH:mm:ss'))
setTimeout(() => {
  console.log('任务2', moment(new Date()).format('HH:mm:ss'))
}, 1000)

//moment(new Date()).format('HH:mm:ss') 如果你是初学者不明白moment()是什么,直接复制代码发现报错,没关系。并不影响你继续学习,这条代码的功能就是输出博主当前执行代码的时间,你可以删掉这条代码,只输出'交警处理事故1'即可。感兴趣的同学可以自行搜索moment的使用方法学

可以看到任务2比任务1 的答应时间晚了一秒

前面我们说到定时器是一个异步的代码,我们来验证一下。

setTimeout(() => {
  console.log('任务2', moment(new Date()).format('HH:mm:ss'))
}, 1000)
console.log('任务1', moment(new Date()).format('HH:mm:ss'))

按照代码自上而下的执行顺序,在控制台中应该是一秒钟以后先输出的任务2再输出的任务1

从控制台的输出得知并不是这样的。代码跳过了任务2先输出的任务1,然后一秒钟之后才输出的任务2,证实定时器确实是一个异步的代码,它并没有按照顺序执行。

异步代码的好处就在于不会造成代码的堵塞,例如:现在我们是交通参与者的身份开着车行驶在路上,突然前方出现了交通事故,此时边上有辅道可以绕过去,难道我们就要等到这起交通事故处理完成之后才能通行吗?当然是没有这个必要的,我们可以先通过边上的辅道绕过去,无需等到交通事故处理完毕之后再通行,如果等到交通事故处理之后再通行那就会造成交通瘫痪大堵车的结局。

还有的时候异步任务也需要同步的去执行。还是以这个交通事故举例子,例如:我们是交警,我们接到了调度中心发来的任务请求,告知我们某路段发生交通事故需要我们前往现场进行处理。在接警没多久之后调度中心又发来请求,告知我们现在警力紧张,在我们的不远处还有一起交通事故希望我们处理完手里的这个事故之后立马前往下一个路段处理第二起交通事故。那此时任务二就要等待任务一执行完毕之后才能执行。其他的交通参与者当然不需要等待交警的任务执行完毕之后才能通过,可以自行从边上绕过去。上代码,我们把这个小故事以代码的形式呈现出来

console.log('社会车辆1')
console.log('社会车辆2')
setTimeout(() => {
  console.log('交警处理事故1', moment(new Date()).format('HH:mm:ss'))
  setTimeout(() => {
    console.log('交警处理事故2', moment(new Date()).format('HH:mm:ss'))
  }, 1000)
}, 1000)
console.log('社会车辆3')
console.log('社会车辆4')

 

通过控制台的输出我们可以看到社会车辆1、2、3、4先行通过了,交警处理事故1之后一秒钟事故2才执行处理。

但其实这种写法并不美观,如果有很多事故需要处理呢,那是不是就要嵌套很多层呢。

console.log('社会车辆1')
console.log('社会车辆2')
setTimeout(() => {
  console.log('交警处理事故1', moment(new Date()).format('HH:mm:ss'))
  setTimeout(() => {
    console.log('交警处理事故2', moment(new Date()).format('HH:mm:ss'))
    setTimeout(() => {
      console.log('交警处理事故3', moment(new Date()).format('HH:mm:ss'))
      setTimeout(() => {
        console.log('交警处理事故4', moment(new Date()).format('HH:mm:ss'))
        setTimeout(() => {
          console.log('交警处理事故5', moment(new Date()).format('HH:mm:ss'))
          setTimeout(() => {
            console.log('交警处理事故6', moment(new Date()).format('HH:mm:ss'))
          }, 1000)
        }, 1000)
      }, 1000)
    }, 1000)
  }, 1000)
}, 1000)
console.log('社会车辆3')
console.log('社会车辆4')

这样的写法会导致代码的可读性非常的差,你可能说我现在一眼就能看出哪儿条输出语句对应着哪儿个代码块。没错,就以现在的这个代码是可以做到。但如果当每一个代码块中的代码复杂起来多起来了以后呢。你还能一眼就看出来吗?当然是很费力的嘛。这就是我们常说的回调地狱。

Promise的出现就可以很好的规避掉这个问题,让我们来进入Promise的学习吧。 

new Promise()

Promise就长这样↑

Promise接收一个对象,在对象中接收两个参数:resolve和reject

new Promise((resolve, reject) => {})

resolve和reject是由Promise对象传入的。resolve直译过来的意思是:决定、解决,是在程序执行成功的时候调用的,而reject直译过来的意思是:拒绝,是在程序调用失败的时候调用的,故此你可以将resolve理解为成功,reject理解为失败。

例如:现在我们正在登陆某一个网站,当你的用户名或密码输入错误之时,身份验证不通过就会返回错误信息,此时就可以调用reject。反之用户名和密码都正确身份认证通过了,此时便可调用resolve。

咱们先打印一下这个Promise对象,看看长什么样

const promiseObj = new Promise((resolve, reject) => {})

console.log(promiseObj)

我们看到在控制台打印出来的数据中有一个pending等待的意思,promise对象中有一个状态的概念。你看现在的状态是一个默认状态pending,你可以理解为这个promise里的resolve和reject一个都没有被触发。

我们来触发resolve参数看看状态

const promiseObj = new Promise((resolve, reject) => {
  resolve()
})
console.log(promiseObj)

观察发现PromiseState从pending等待变成了fulfilled,fulfilled就表示完成或者成功了。

我们再来触发一下reject参数看看状态

const promiseObj = new Promise((resolve, reject) => {
  reject()
})
console.log(promiseObj)

观察发现PromiseState的状态变成了rejected 表示拒绝或者失败了

promise对象给我们提供了三个方法,咱们自己可以如图一样通过对象点的形式来看看是不是出来catch 、finally、then这三个方法。

简单粗的讲一下这三个方法。还是以登陆某一网站为例:假设用户名和密码都填写正确则会走到then()方法里,你可以理解为then()方法是成功时候调用的。假设用户名和密码有一个或者全都填写错误时服务器便会返回错误信息,此时代码就会走到catch()方法里,你可以理解为catch()方法是在错误时候调用的。那么finally()方法则是无论服务器返回的是true还是false都会走到finally里面,你可以理解为不论对错都会调用finally。用户名密码填写正确,走完then()还会走finally。用户名密码填写错误,走完catch()也还会走finally()。

让我们来一一验证一下:

const promiseObj = new Promise((resolve, reject) => {
  resolve('身份认证通过!')
})
promiseObj
  .then((data) => { //data就是resolve传过来的内容 名称没有规定自定义即可
    console.log(data)
  })
  .catch((error) => {
    console.log(error)
  })
  .finally(() => {
    console.log('我是finally')
  })

通过代码可以看得出这三个方法接收的都是一个回调函数。通过控制台的输出可以得知当我们调用了resolve时代码走到了then()方法之后又走到了finally()方法,验证通过。

const promiseObj = new Promise((resolve, reject) => {
  reject('身份认证失败!')
})
promiseObj
  .then((data) => { 
    console.log(data)
  })
  .catch((error) => { //error就是reject传过来的内容  名称没有规定自定义即可
    console.log(error)
  })
  .finally(() => {
    console.log('我是finally')
  })

通过控制台的输出可以得知当我们调用了resolve时代码走到了catch()方法之后又走到了finally()方法,验证通过。

到这儿为之你已经对Promise的使用有了一个简单的认识。我们现在用Promise来解决一下前面举例说明的小故事。看看你是否会觉得优雅很多,代码更可读。

虽然promise本身是同步的,但是promise的.then() .catch() . finally()这些方法中的回调是异步的,所以在这里我们就不用定时器了。

console.log('社会车辆1')
console.log('社会车辆2')
const promiseObj = new Promise((resolve, reject) => {
  resolve('交警处理事故1')
})
promiseObj
  .then((data) => {
    console.log(data)
    return new Promise((resolve, reject) => {
      resolve('交警处理事故2')
    })
  })
  .then((data) => {
    console.log(data)
    return new Promise((resolve, reject) => {
      resolve('交警处理事故3')
    })
  })
  .then((data) => {
    console.log(data)
    return new Promise((resolve, reject) => {
      resolve('交警处理事故4')
    })
  })
  .then((data) => {
    console.log(data)
  })
  .catch()
console.log('社会车辆3')
console.log('社会车辆4')

有得人会说 这也嵌套了呀。没错这确实是也嵌套了,但是只有一层,无论多又少次都只有一层

promiseObj.then().then().then().then().then().then().catch()

↑这样看是不是就更直观了呢。

还有另一种写法可以给每一个.then()都设置一个单独的catch() 直接上代码

从这张图↑可以看到.then()里面其实可以传递两个参数的,第一个表示成功时候的调用,第二个表示失败时候的调用

promiseObj
  .then((data) => {}, (error) => {})
  .then((data) => {}, (error) => {})
  .then((data) => {}, (error) => {})
  .then((data) => {}, (error) => {})
  .then((data) => {}, (error) => {})

↑这便是大致的一个结构 

const promiseObj = new Promise((resolve, reject) => {
  resolve('交警处理事故1')
})
console.log('社会车辆1')
console.log('社会车辆2')
promiseObj
  .then(
    (data) => {
      console.log(data)
      return new Promise((resolve, reject) => {
        resolve('交警处理事故2')
      })
    },
    (error) => {
      console.log(error)
    }
  )
  .then(
    (data) => {
      console.log(data)
      let errorBtn = false
      return new Promise((resolve, reject) => {
        if (errorBtn) {
          resolve('交警处理事故3')
        } else {
          reject('交通事故处理时候遇到特殊状况')
        }
      })
    },
    (error) => {
      console.log(error)
    }
  )
  .then(
    (data) => {
      console.log(data)
      return new Promise((resolve, reject) => {
        resolve('交警处理事故4')
      })
    },
    (error) => {
      console.log(error)
    }
  )
  .then()

console.log('社会车辆3')
console.log('社会车辆4')

我们在交警事故处理3出加了一个reject的调用

通过控制台的输出可以得知咱们的reject起作用了。当交警处理事故3处报错时代码就终止了,后面的程序就不执行了。

以上就是本章的知识点讲解分享,感谢大家得耐心观看学习,欢迎大家在评论区讨论纠错,与大家共勉。

  • 42
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值