async 与 await

120 阅读4分钟

1.async-await作用

1-1.使用场景

用同步的方式,执行异步操作

例如:有3个请求需要一次进行,而且第2、第3个请求都要用到前一个请求返回的结果

promise实现:

function request(param) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            const res = param *2
            console.log(res)
            resolve(res)
        }, 1000);
    })
}
function dataRequest(param) {
    console.log('promise实现:')
    request(10).then(res1 => {
        request(res1).then(res2 => {
            request(res2).then(res3 => {
                console.log('请求结束', res3)
            })
        })
    })
}
dataRequest()

image.png

async-await实现:

function request(param) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            // console.log(param)
            const res = param *2
            console.log(res)
            resolve(res)
        }, 1000);
    })
}
async function dataRequest() {
    console.log('async-await实现:')
    const res1 = await request(10)
    const res2 = await request(res1)
    const res3 = await request(res2)
    console.log('数据请求结束', res3)
}
dataRequest()

image.png

1-2.使用注意事项

1-2-1.await只能在async函数中使用,不然会报错。

function request(param) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            const res = param *2
            console.log(res)
            resolve(res)
        }, 1000);
    })
}
function dataRequest() {
    await request(10)
}

1-2-2.如果await后面函数的返回值不是一个promise,则起不到排队等待的作用。

function request(param) {
    setTimeout(() => {
        const res = param *2
        console.log(res)
    }, 1000);
}
async function dataRequest() {
    await request(10)
    await request(20)
    await request(30)
}
dataRequest()
// 一次性输出20、40、60

1-2-3.async函数的返回值是一个fulfilled状态的promise

async function dataRequest() {}
console.log(dataRequest())

2.generator函数。

2-1.generator函数简介

在定义一个函数的时候,在函数名前面加上*,此时这个函数就变成了一个generator函数。

例如:

function* gen() {}

2-2. yield

2-2-1. yield 基本使用

只有在 generator 函数中才能使用 yield ,它相当于函数运行过程中的暂停点,想要函数继续下走,需要使用 next 方法。 例如:

function* gen() {
    yield console.log(1)
    yield console.log(2)
    yield console.log(3)
}
const g = gen()
g.next() // 1
g.next() // 2
g.next() // 3

2-2-2. next 方法返回值

next 方法返回一个对象,对象中有 valuedone 两个属性。

value : yield 后面表达式的返回值。

done : 标志 generator 函数是否已走完,没走完为false,走完为true

例子:

function* gen() {
    yield 1
    yield 2
    yield 3*3
}
const g = gen()
console.log(g.next()) // { value: 1, done: false }
console.log(g.next()) // { value: 2, done: false }
console.log(g.next()) // { value: 9, done: false }
console.log(g.next()) // { value: undefined, done: true }
function* gen() {
    yield 1
    yield 2
    yield 3*3
    return 10
}
const g = gen()
console.log(g.next()) // { value: 1, done: false }
console.log(g.next()) // { value: 2, done: false }
console.log(g.next()) // { value: 9, done: false }
console.log(g.next()) // { value: 10, done: true }

注意:最后一个next的函数值的value属性是否为undefined取决于函数gen是否有返回值。

2-2-3.next函数传参

调用next方法的时候,可以向其中传参,这个参数是yield的返回值。

例子:

function* gen(param) {
    console.log(param)
    const param1 = yield 1
    console.log(param1)
    const param2 = yield 2
    console.log(param2)
    const param3 = yield 3*3
    console.log(param3)
    return 10
}
const g = gen('-1')
g.next('00') 
g.next('11')
g.next('22')
g.next('33')
// -1
// 11
// 22
// 33

注意,第一次调用next时传参无效,从第二次调用next传参才有效。

2-2-4.promisenext传参结合使用

function createPromise(param) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            const res = param * 2
            resolve(res)
        }, 1000);
    })
}
function* gen() {
    const res1 = yield createPromise(1)
    const res2 = yield createPromise(res1)
    const res3 = yield createPromise(res2)
    return res3
}
const g = gen()
g.next().value.then(res => {
    console.log(res) // 2
    g.next(res).value.then(res => {
        console.log(res) // 4
        g.next(res).value.then(res => {
            console.log(res) // 8
        })
    })
})
// 2
// 4
// 8

3.实现async-await

3-1.简单实现

function createPromise(param) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            const res = param * 2
            resolve(res)
        }, 1000);
    })
}
function* gen() {
    const res1 = yield createPromise(1)
    const res2 = yield createPromise(res1)
    const res3 = yield createPromise(res2)
    return res3
}

function myAsyncFun(genFun) {
    return function() {
        return new Promise((resolve, reject) => {
            const g = genFun()
            g.next().value.then(res => {
                console.log(res)
                g.next(res).value.then(res => {
                    console.log(res)
                    g.next(res).value.then(res => {
                        resolve(res)
                    })
                })
            })
        })
    }
}
const asyncFun =  myAsyncFun(gen)
asyncFun().then(res => {
    console.log('res', res)
})

3-2.完善代码

function createPromise(param) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            const res = param * 2
            resolve(res)
        }, 1000);
    })
}
function* gen() {
    const res1 = yield createPromise(1)
    const res2 = yield createPromise(res1)
    const res3 = yield createPromise(res2)
    return res3
}


function generatorToAsync(generatorFn) {
  return function() {
    const gen = generatorFn.apply(this, arguments) // gen有可能传参

    // 返回一个Promise
    return new Promise((resolve, reject) => {

      function go(key, arg) {
        let res
        try {
          res = gen[key](arg) // 这里有可能会执行返回reject状态的Promise
        } catch (error) {
          return reject(error) // 报错的话会走catch,直接reject
        }

        // 解构获得value和done
        const { value, done } = res
        if (done) {
          // 如果done为true,说明走完了,进行resolve(value)
          return resolve(value)
        } else {
          // 如果done为false,说明没走完,还得继续走

          // value有可能是:常量,Promise,Promise有可能是成功或者失败
          return Promise.resolve(value).then(val => go('next', val), err => go('throw', err))
        }
      }

      go("next") // 第一次执行
    })
  }
}

const asyncFn = generatorToAsync(gen)

asyncFn().then(res => console.log(res))

参考文献:

7张图,20分钟就能搞定的async/await原理!为什么要拖那么久?