promise对象与async/await的简单理解

promise对象的简单介绍

promise对象构造方法很简单,只需要实例化Promise即可。
resolve与reject在实例化过程中由js引擎自动形成传入,resolve会将promise内部状态改变为完成的状态并将处理后的结果赋值给result然后等待使用,reject则会在发生错误时将状态改为错误,将错误对象赋值给result。

let promise = new Promise(function (resolve, reject) {
  // 要处理的任务
})

对于promise处理后的结果我们可以通过.then()和.catch()来使用
then内部可以传入两个处理函数来对结果进行处理,第一个为处理正确结果的函数,第二个为处理错误信息的函数,只传入一个函数则只进行正确结果处理
then还可以处理任何带有then方法的结果并且then的返回值也会自动包装成promise对象

promise.then((result) => {}, (err) => {})

catch只需要传入一个函数作为参数,可以接受前面所有的错误信息进行处理。

promise.then((result) => {}).catch((err) => {})

catch还可以将错误抛出,让后面的错误处理程序进行处理

async/await简单理解

async实际上是对于函数的返回结果进行promise化

async function () {
  return 1
}
//等同
new Promise(function (resolve, reject) => {
  resolve(1)
})
//等同
promise.resolve(1)//promise的简单写法

await顾名思义就是等待promise处理结果,我们可以用一个变量接收这个返回值,await只能在async里面使用,所以我们可以很优雅的使用promise解决问题而不用使用then()方法

promise对象为什么可以解决回调地狱问题

promise对象可以解决回调地狱问题,避免为了顺序执行任务而造成的回调函数嵌套,关键点在于promise不用立即调用可以创建后在任何位置来通过方法处理获得的结果,并且利用链式调用的技巧来实现异步加载顺序执行。

  1. 回调函数方法
  setTimeout(() => {
    console.log(3)
    setTimeout(() => {
      console.log(2)
      setTimeout(() => {
        console.log(1)
      }, 1000)
    }, 2000)
  }, 3000)
  //3 2 1
  1. promise方法
function p1() {
  return new Promise(function (resolve, reject) {
    setTimeout(() => {
      resolve('3')
    }, 3000)
  })
}
function p2() {
  return new Promise(function (resolve, reject) {
    setTimeout(() => {
      resolve('2')
    }, 2000)
  })
}
function p3() {
  return new Promise(function (resolve, reject) {
    setTimeout(() => {
      resolve('1')
    }, 1000)
  })
}
//链式调用顺序执行
p1().then((r1)=> {
	 console.log(r1)
	 return p2()
  })
	.then((r2)=> {
	  console.log(r2)
	  return p3()
	})
	.then((r3) => {
	  console.log(r3)
  })
  //3 2 1
  1. async/await方法
async function run() {

  function fn(str, time) {
    return new Promise(function (resolve, reject) {
      setTimeout(() => {
        resolve(str)
      }, time)
    })
  }

  let p1 = await fn('3', 3000)
  let p2 = await fn('2', 2000)
  let p3 = await fn('1', 1000)

  console.log(p1)
  console.log(p2)
  console.log(p3)
}
run()
//3 2 1

虽然看起来代码变得很多,但是确实清晰了很多,尤其是当嵌套更加复杂业务逻辑步骤变多时优点显而易见,并且我们可以随意的改变执行顺序,让结果按照自己的需求来处理

promise对象在事件循环中的状态

promise实例化过程中其实内部代码也是在主队列中顺序执行的,当遇到同步代码就执行,异步代码就放到事件队列里排队,但是对promise对象进行处理时这些处理程序则是异步的但是又不会单独放到异步事件队列中,而是放在了微任务队列中,即表现为宏任务队列执行完毕立即执行微任务队列中的代码。
但是根据promise情况不同微任务的位置也不同,如果promise里面立即设置了resolve则then/catch处理程序会放到主任务队列的微任务队列里,如果promise内使用异步操作来设置resolve时只有浏览器开始执行这个异步操作,then/catch处理程序才会放到任务队列的微任务队列中,所以没有轮到执行异步操作时主任务队列微任务为空,只有浏览器将这个异步操作放到任务队列中时,才会有微任务出现

function hi() {
  let a;
  alert(1)
  let promise = new Promise(function (resolve, reject) {
    alert(2)
    resolve('ok')
  })
  promise.then((result) => {
    alert(a)
    a = result
  })
  alert(a)//undefined
  a = 'no'
  setTimeout(() => {
    alert(a)//ok
  }, 1000)
}

hi()
//1 2 undefined no ok

首先代码顺序执行打印1然后执行到new Promise里面打印2,将result设为ok,遇到then放到微任务里,继续向下执行,因为a未赋值打印undefined,将a赋值为no,遇到定时器,放到异步队列中,此时执行微任务,打印a因为设为了no打印出no,然后把a的值改为promise的result结果即ok,微任务执行完毕,执行异步程序,打印出ok。

setTimeout(() => {
  alert(5)
}, 5000)
setTimeout(() => {
  alert(1)
}, 1000)

let promise = new Promise(function(resolve, reject) {
  alert(2)
  setTimeout(() => {
    resolve('ok')
  }, 2000)
})
promise.then((result) => {
  alert(result)
})
// 2  1 ok 5

// setTimeout(() => alert(5),5000)
// setTimeout(() => alert(2), 2000)
// setTimeout(() => alert(1), 1000)
//1 2 5

显而易见,因为promise中的异步操作放到了异步队列中,此时主任务队列并不会执行then的操作,只有当异步操作被放到任务队列中时,才会出现微任务。下面定时器作为参照结果。

文档

现代javascript教程
MDN