lhlGitHub / trisome

前端大厂进攻学习资料库
21 stars 1 forks source link

async/await 原理是什么? #4

Open lhlGitHub opened 2 years ago

yanggengzhen123 commented 2 years ago

Day4

async/await作用是用同步方式,执行异步操作 async/await实际上是用到了promise+ generator语法糖来实现的 通过yield来暂停,然后执行promise返回状态,通过内部执行.then的方式返回res给下一个next达到传参的目的,最后返回.next().value的promise状态

zhanglongLee commented 2 years ago

async/await 原理是什么?

概念:

async/await其实是Promise的语法糖,它是为优化then的嵌套调用而开发出来的。从字面上来看,async是“异步”的简写,await译为等待,所以我们很好理解async声明function是异步的,await等待是一个Promise对象,或者是其他任何值,并且await会暂停当前async function的执行,等待Promise的处理完成。

async声明的异步函数,把return后面的值通过Promise.resolve()返回Promise对象。

await如果等待的是Promise对象,则返回Promise的处理结果;如果是其他值,则返回该值本身。若Promise正常处理(fulfillded),其将回调的resolve函数参数作为await表达式的值;若Promise处理异常(rejected),await表达式会把Promise异常原因抛出,await表达式的值为undefined,并且await表达式后面的代码被暂停了;

实现原理

async/await是一种语法糖,用到的是ES6里的迭代函数——generator函数

generator函数

generator函数跟普通函数在写法上的区别就是,多了一个星号*,并且只有在generator函数中才能使用yield,什么是yield呢,他相当于generator函数执行的中途暂停点,比如下方有3个暂停点。而怎么才能暂停后继续走呢?那就得使用到next方法next方法执行后会返回一个对象,对象中有value 和 done两个属性

function* gen() {
  yield 1
  yield 2
  yield 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: 3, done: false }
// 可以看到最后一个是undefined,这取决于你generator函数有无返回值
console.log(g.next()) // { value: undefined, done: true }

实现async/await

// 模拟异步操作
function fn(nums) {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve(nums * 2)
        }, 1000)
    })
}
// 生成器函数
function* gen() {
    console.time('test')
    const num1 = yield fn(1)
    const num2 = yield fn(num1)
    const num3 = yield fn(num2)
    console.timeEnd('test')
    return num3
}
function generatorToAsync(generatorFn) {
    return function () {
        // generatorFn有可能传参
        const gen = generatorFn.apply(this.arguments)
        // 模拟async函数返回一个Promise对象
        return new Promise((resolve, reject) => {
            // 定义一个go函数,用来自动执行 gen.next() 方法
            function go(key, arg) {
                let res
                try {
                    // 相当于res = gen.next('参数')
                    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 genToAsync = generatorToAsync(gen)// 生成async函数构造器函数
const asyncRes = genToAsync()// 生成async函数
console.log(asyncRes);// async函数返回的是Promise
asyncRes.then(res=>{
    // test: 3032.6279296875 ms
    console.log(res);// 3秒后输出8
})

参考文章