Javascript async/await 原理 & 实现
分类于 Javascript
发表于 2018-03-01
作者: 灼灼团队
本文字数: 1763
阅读时长 ≈ 5.9 分钟

ES7 在语法层面上支持了 async/await 关键字,我们将在代码层面上模拟实现 async/await 来理解它的工作原理。

本文旨在说明其工作原理,你应该具备以下前置知识:

  • 熟悉 Promise 的概念及使用
  • 熟悉 async/await 的概念及使用
  • 了解 Generator 的概念

# Generator 基础复习

首先复习一下 Js Generator 的基本用法:

function * work() {
    const r = yield 'hi'
    console.log(r)      // 'ha'
    return
}

const gen = work()
console.log(gen)        // Object [Generator] {}

let result = gen.next()
console.log(result)     // { value: 'hi', done: false }

result = gen.next('ha')
console.log(result)     // { value: undefined', done: true }

// output summary:
// Object [Generator] {}
// { value: 'hi', done: false }
// ha
// { value: undefined', done: true }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

我们调用 work() 这个函数时它返回的是一个 Generator 对象 gen; 然后我们调用 gen.next() 返回值是 { value: 'hi', done: false }, 其中 value 字段就是 yield 关键字返回给我们的数据,done 字段表示该 Generator 函数当前并没有执行结束,并且暂停在了 yield 所在的这一行。

当我们第二次调用 gen.next('ha') 的时候,Generator 函数继续执行,并将我们传给 next() 函数的参数值 'ha' 返回并赋值给变量 r,然后继续运行并打印出 r,直到遇到 return 语句,函数返回 { value: undefined', done: true } 给我们。

其中 value 是返回值, done 表示 Generator 函数已执行结束并返回。

Generator 函数的一个特点是可以使用 yeild 关键字暂停当前函数的执行,并向调用者返回相应数据以及自己的运行状态( 代码中的 done 字段)。 另外一个特点是,调用者通过 next() 函数恢复 Generator 执行时可以通过参数传数据给 Generator 作为 yield 的返回值 (赋给代码中的变量 r)。

# run()

有了前面关于 Generator 知识的复习,我们实现本文的核心函数; 它接收一个 Generator 函数 fn 作为参数,它的功能就是模拟 async/await 机制运行该 fn 函数。

function run(fn) {
    const gen = fn()
    function _next(data){
        const { value, done } = gen.next(data)
        if(done) return value

        if(value.then){
            value.then(data => _next(data))
        }else{
            _next(value)
        }
    }
    _next()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

在解释我们这个 run() 函数之前,先看一下它的用法示例:

function sleep(duration) {
    return new Promise((resolve, reject) => {
        setTimeout(() => resolve(`slept ${duration}ms`), duration)
    })
}

function * work () {
    const ret = yield sleep(1000)
    console.log(ret)
    const ret2 = yield sleep(2000)
    console.log(ret2)
}

run(work)
// output
// slept 1000ms
// slept 2000ms
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

看了这个示例代码,应该就能明白 run() 的作用了,实际上通过这个函数,我们已经模拟实现了 async/await 机制;

我们做个不严谨的类比来说明:

  • function * work() { } 中间的 * 号就相当于 async 关键字的作用;
  • yield 关键字就相当于 await 关键字;

# 总结

实际上 async/await 关键字也可以认为是 ES7 的语法糖,我们已经通过 run() 函数来模拟了其工作原理,至于 Js 引擎具体怎么实现的,未来我们写 分析 v8 引擎实现相关文章的时候再细究。

联系我们
联系电话:17681177133
联系邮箱:admin@zhuo-zhuo.com
公司地址:合肥市高新区习友路2666号 (习友路和石莲南路交叉口西北角)二期304室
官网
博客
皖ICP备20009670号-2
合肥灼灼信息技术有限公司 | Copyright © 2020-present zhuo-zhuo.com