async/await的实现原理(手动实现)
- IT业界
- 2025-07-21 19:21:34

为什么要引入async/await操作符?
对于js的异步编程场景,无论是使用xhr回调还是promise回调,当异步操作过多并且每个动作之间存在依赖关系(即需要串行执行)时,代码的可读性和维护性会变得很差。async/await以同步代码的形式很好地解决了这个问题。
因为async/await的底层实现使用generator和promise,所以首先要了解generator的原理。
生成器函数(generator) function* genDemo() { console.log("开始执行第一段") yield 'generator 2' console.log("开始执行第二段") yield 'generator 2' console.log("开始执行第三段") yield 'generator 2' console.log("执行结束") return 'generator 2' } console.log('main 0') let gen = genDemo() console.log(gen.next().value) console.log('main 1') console.log(gen.next().value) console.log('main 2') console.log(gen.next().value) console.log('main 3') console.log(gen.next().value) console.log('main 4')带*号的函数就叫生成器函数,是可以暂定执行和恢复执行的。
在执行过程中,如果遇到yield关键字,函数会返回关键字后面的内容给外部,然后暂停执行。
外部可以使用next方法来恢复执行该函数。
v8是如何实现函数的执行和恢复的呢?这里需要引入协程的概念。
协程协程是一种比线程更轻量级的存在,可以理解为是在线程上运行的任务。一个线程可以存在多个协程,但同时只能执行一个协程。如果当前执行的是A协程,要启动B协程就需要暂停A协程,把js主线程的控制权交给B协程,这样表现为A协程暂停,B协程开始执行,此时我们把A协程称为B协程的父协程。
分析上面的示例代码,执行过程如下:
js主线程执行genDemo方法,生成gen协程。js主线程执行gen.next方法,此时js引擎会保存当前主线程(父协程)调用栈信息,开始执行gen协程。gen协程碰上yield关键词,会保存当前gen协程的调用栈信息,并将关键词后的内容返回给父协程,并暂定执行gen协程,恢复执行父协程(即外部代码);后续反复执行2和3的步骤,直接主线程代码执行完。结论:
gen协程和父协程是交替执行的,它们通过yield和next配合完成切换。
async/awaitasync:
async function foo() { console.log(1); return 2 } var p = foo(); console.log(p); // 1 // Promise { 2 }从以上代码可以得知,async声明后的函数返回了一个Promise对象,状态是resolved,上述代码等价于
function foo() { console.log(1); return new Promise(resolve => resolve(2)) } var p = foo(); console.log(p);await:
async function foo() { console.log(1) let a = await 100 console.log(a) console.log(2) } console.log(0) foo() console.log(3) // 0 // 1 // 3 // 100 // 2分析执行过程:
输出0;主线程保存父协程调用栈信息,开始执行foo协程,输出1;遇到await操作符,await操作符干了很多事情。 let a = await 100;首先相当于执行了new Promise(resolve => resolve(100)); 然后将主线程控制权交给父协程,同时将该promise对象返回给父协程;父协程调用promise对象的then方法,监控promise状态的改变。输出3,宏任务队列执行完成;检查微任务队列,执行promise对象的then方法回调,并将返回值传给foo协程,并执行foo协程;输出100,输出2;完成; 手动实现async、await
1. 创建启动函数,**传入生成器函数**(判断传入值是否生成器函数)
2. 创建执行函数(**接收一个参数**,每次 next 后的返回值 => { value , done })
- 通过判断 v.done 决定递归是否结束
- 递归执行自身,并判断返回值,如果是 **promise对象** 就调用 .then 等待异步执行后,执行 next 并将 promise 结果传入;如果是**基本类型**就直接执行 next 函数,传入 value 值即可
3. **执行启动函数**,传入(generator.next())
// promise 函数 function getData (data, time = 1000) { // 返回 promise 对象 if (typeof data === 'object') { return new Promise(function (resolve, reject) { setTimeout(function () { resolve(data) }, time) }) } // 返回普通类型 else { return data } } // function* => async function function* test () { let res1 = yield getData("字符串"); // yield => await console.log("res1", res1); let res2 = yield getData({ data: [1, 2, 3] }); console.log("res2", res2); let res3 = yield getData({ data: { a: 1 } }, 3000); console.log("res3", res3); let res4 = yield getData(true); console.log("res4", res4); } // async await 底层原理:启动一个自执行的生成器函数 function start (fn) { // 1 判断是否传入生成器函数 if (fn.constructor.name != 'GeneratorFunction') { throw new Error('it is not a generator function.') } // 2 执行获取生成器对象 let generator = fn() // 3 创建一个执行函数(val => 执行 next 函数后的值) let execute = (val) => { // 1 递归结束 if (val.done) return; // 2 判断类型 if (val.value instanceof Promise) { // 3 递归执行 val.value.then(res => { execute(generator.next(res)) }, err => { generator.throw(err) }) } else { // 3 递归执行 Promise.resolve(val.value).then(res => { execute(generator.next(res)) }) } } // 4 启动执行函数 execute(generator.next()) } console.log(1); start(test) console.log(2);
async/await的实现原理(手动实现)由讯客互联IT业界栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“async/await的实现原理(手动实现)”