async 的原理

119 篇文章 3 订阅
91 篇文章 0 订阅

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 函数不同于普通函数的另一个地方,即执行它不会返回结果,返回的是指针对象。调用指针gnext方法,会移动内部指针(即执行异步任务的第一段),指向第一个遇到的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)
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值