Promise、async、await

3,766 阅读3分钟

1.async关键字的作用

async是异步函数的标识符,说明该函数是个异步函数,返回值是个promise对象。

function fn1() {
  return 1
}
console.log(fn1()) //1

async function fn2() {
  return 1
}
console.log(fn2()) // Promise{<fulfilled>:1}

从上面例子可以看到,async函数的返回值是一个promise对象,既然是一个promise对象,那就自然可以使用其原型上的属性,比如thencatch等等。

fn2()
.then(r => {
  console.log(r) // 1
})


tips:经试验,即使在fn2中return Error('2')也会走到then方法中,是不是说明async函数默认返回的promise状态就是fulfilled?

2.await是什么?

awaitasync wait的缩写,它等待返回的是一个表达式,不管是不是promise对象都可以,只是说如果返回的是promise对象执行的状态不一样而已,需要注意的是await只能在async函数中使用,看下面例子:

function sync() {
  setTimeout(() => {
    return 1
  }, 1000)
}

async function async1(){
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(1)
    }, 2000)
  })
}

async function test(){
  await sync() //undefined
  console.log(1)
}

async function test2() {
  await async1()
  console.log(1)
}

test()
test2()

分别执行testtest2函数可发现,在执行test的时候直接打印了1,在执行test2的时候是过了2秒才打印出1。

可以看出,如果await等来的是一个promise对象,它会"阻塞"后面的代码,直到这个promise对象有返回结果,不管这个结果是成功还是失败。

如果不是一个promise对象那么它就是一个同步函数,按照js规则同步执行。

如果不是一个promise对象,那await后的表达式就是要等待的东西

之前在这里有个理解错误的地方:我以为如果await后面如果不是一个promise对象那么它就是一个同步函数,直接执行后面的代码,其实不是这样,就算不是promise对象那么await后面的内容还是相当于在then执行,跟promise的区别在于如果等待的是一个promise对象,那么要等待这个对象解析完成,如果没有resolve或者reject那么后面的内容就不会执行,看两个例子:

// eg1
function fn1() {
  return new Promise(()=> {
  
  })
}

async function fn2() {
  await fn1()
  console.log('wait fn1') // 这里的值永远也不会打印,因为函数fn1这个promise对象的状态没有解析
}
fn2()


// eg2
async function fn2() {
    await 2
    console.log(24) // 先打印
}
fn2()
console.log('this') // 后打印

//等同于
 async function fn2() {
    Promise.resolve(2)
    .then(r => {
      console.log(24)
    })
 }
 fn2()
 console.log('this')

可以注意到我在test函数中注释了一个undefined这是因为await后的表达式如果是一个函数,它就会立即执行,在sync函数立即执行后返回的就是undefined,它不会再等待定时完成后返回的值

  • 关于await返回值

1.await后是一个promise对象,如果是resolve状态,值就是resolve参数。如果是reject状态,会将错误抛出

// resolve
let p = await Promise.resolve(3)
console.log(p) // 3
// reject
let p = await Promise.reject('error')
console.log(p) // 控制台报错

2.await后不是promise对象,则返回值就是该值的本身

let p = await 3
console.log(p) // 3

3.错误捕获

Promise除了resolve之外还有可能是reject,那么怎么来处理reject呢?

  • 使用try catch捕获
function fn() {
   return Promise.reject('error')
}

async function asyncFn() {
    try {
    	await fn()
        console.log(1) // 如果上一行出错将直接跳到catch,不会执行到这里
    } catch(e) {
    	console.log(e) // error
    }
}

这里可以将可能出现错误的代码放到try中一旦程序出错,try后面的代码将不会被执行,直接跳到catch中去

  • 使用promise.catch捕获
function fn() {
   return Promise.reject('error')
}

async function asyncFn() {
    await fn().catch(e => console.log(e))
    // do something
}

4.使用场景

在业务开发当中,我们可能会遇到比如说,你需要等待A接口请求完成后去执行某个操作,如果不使用await的话可以直接将操作插入的请求的then链当中,我们来对比下两种写法。

// 不使用await
function http() {
    axios.get('url')
    .then(r => {
      doIt()
    })
    .catch(e => {
      console.log(e)
    })
}

function doIt() {
  // do something
}

// 使用await

function http() {
  return new Promise((resolve, reject) => {
    axios.get('url')
    .then(r => {
      resolve('success')
    })
    .catch(e => {
      reject('error')
    })
  })
}

function doIt() {
  // do something
}

async function fn() {
    let p = await http()
    if (p === 'success') {
      doIt()
    } else {
     ....
    }
}

fn()