写点什么

5 分钟带你彻底掌握 async 底层实现原理!

作者:千锋IT教育
  • 2022-12-15
    北京
  • 本文字数:1748 字

    阅读完需:约 6 分钟

ES2017 标准引入了 async 函数,使得异步操作变得更加方便。

async 函数是什么?一句话,它就是 Generator 函数的语法糖。研究 async 的原理,就必须先弄清楚 Generator 是个啥。

Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。

形式上,Generator 函数是一个普通函数,但是有两个特征。一是,function 关键字与函数名之间有一个星号;二是,函数体内部使用 yield 表达式,定义不同的内部状态(yield 在英语里的意思就是“产出”)

看一个例子:

function* gen(x) { var y = yield x + 2; return y;}var g = gen(1);g.next() // { value: 3, done: false }g.next() // { value: undefined, done: true }复制代码
复制代码

上面代码中,调用 Generator 函数,会返回一个内部指针(即遍历器)g。这是 Generator 函数不同于普通函数的另一个地方,即执行它不会返回结果,返回的是指针对象。调用指针 g 的 next 方法,会移动内部指针(即执行异步任务的第一段),指向第一个遇到的 yield 语句,上例是执行到 x + 2 为止。

换言之,next 方法的作用是分阶段执行 Generator 函数。每次调用 next 方法,会返回一个对象,表示当前阶段的信息(value 属性和 done 属性)。value 属性是 yield 语句后面表达式的值,表示当前阶段的值;done 属性是一个布尔值,表示 Generator 函数是否执行完毕,即是否还有下一个阶段。

这样手工的执行 next()函数,着实有些麻烦,能写个工具让他自动执行吗?那我们就来试试:

封装一个 spawn 函数,返回一个 spawn 函数,给函数传入 Generator 函数作为参数,spawn 实现 next() 方法的执行。

function fn(args) { return spawn(function* () { // ...  });}复制代码
复制代码

spawn 函数的实现:

function spawn(genF) { return new Promise(function(resolve, reject) { const gen = genF(); function step(nextF) { let next; try { next = nextF();      } catch(e) { return reject(e);      } if(next.done) { return resolve(next.value);      } Promise.resolve(next.value).then(function(v) { step(function() { return gen.next(v); });      }, function(e) { step(function() { return gen.throw(e); });      });    } step(function() { return gen.next(undefined); });  });}复制代码
复制代码

应用这个方法执行一下第一个例子:

function fn(x) { return spawn(function* gen() { var y = yield x + 2 return y;  });}fn(1).then((result) => { console.log(result) // 3})复制代码
复制代码

如果 yield 后面是个 Promise, 就可以实现异步了:

function fn(x) { return spawn(function* gen() { var y = yield new Promise((resolve) => { setTimeout(() => { resolve(x + 1)      }, 1000)    }) return y;  });}fn(1).then((result) => { console.log(result) // 过一秒后打印 3})复制代码
复制代码

这样,过一秒后就打印 3 了。

从整个代码上来看,实现起来有些麻烦。Async 简化了一切,使用它,不再需要 spawn 函数,只需将 Generator 函数的星号(*)替换成 async,将 yield 替换成 await,仅此而已。改造一下:

async function fn(x) { let result = await new Promise((resolve) => { setTimeout(() => { resolve(x + 2)    }, 1000)  }) return result}fn(1).then((result) => { console.log(result)})复制代码
复制代码

真是简洁了很多。

最后看一个面试题,如何将程序的执行结果 1,3,2,改造为 1,2, 3

<script>const getUser = () => { return new Promise((resolve) => { setTimeout(() => { resolve(2)    }, 0)  })}export default { methods: { async onGetUser() { getUser().then((result) => { console.log(result)      })    }  }, async created() { console.log(1) await this.onGetUser() console.log(3)  }}</script><template> <div>    hello world </div></template><style lang="scss"> </style>复制代码
复制代码

只需修改一个 onGetUser 函数即可:

async onGetUser() { // getUser().then((result) => { //   console.log(result) // }) let result = await getUser() console.log(result)}复制代码
复制代码

以上就是 async 的原理,你学会了吗?

用户头像

国内IT培训机构良心品牌 2022-08-02 加入

学习资料下载获取,添加QQ:3547925594

评论

发布
暂无评论
5分钟带你彻底掌握async底层实现原理!_千锋IT教育_InfoQ写作社区