async/await:使用同步的方式去写异步代码

(一)async/await 使用了 Generator 和 Promise 两种技术

1. Generator 生成器函数

生成器函数是一个带星号函数,而且是可以暂停执行和恢复执行的函数。

(1)在生成器函数内部执行一段代码,如果遇到 yield 关键字,那么 JavaScript 引擎将返回关键字后面的内容给外部,并暂停该函数的执行。

(2)外部函数可以通过 next 方法恢复函数的执行。

要搞懂函数为何能暂停和恢复,那你首先要了解协程的概念。协程是一种比线程更加轻量级的存在。可以把协程看成是跑在线程上的任务,一个线程上可以存在多个协程,但是在线程上同时只能执行一个协程,比如当前执行的是 A 协程,要启动 B 协程,那么 A 协程就需要将主线程的控制权交给 B 协程,这就体现在 A 协程暂停执行,B 协程恢复执行;同样,也可以从 B 协程中启动 A 协程。通常,如果从 A 协程启动 B 协程,我们就把 A 协程称为 B 协程的父协程。

例子:

// 声明一个构造器函数
    function* generator () {
      console.log("开始执行第一段") 
      yield 1 

      console.log("开始执行第二段") 
      yield {name: 'ss'}

      console.log("开始执行第三段") 
      yield 3

      console.log("执行结束") 
      return 4
    }
    // 声明一个迭代器
    let gen = generator()
    // 执行第一步
    console.log(gen.next()) 

返回一个对象,value 的值是 yield 后的值,done 表示该迭代器是否执行到最后一步

// 执行第二步
console.log(gen.next()) 

 

// 执行第三步
console.log(gen.next()) 

// 执行第四步
console.log(gen.next())

此时的done 变为true ,表示该迭代器执行完毕。

如果再next

console.log(gen.next())

则value 返回 undefined,done 为 true

注意:如果 yield 后没有值 ,value也是undefined

2. 消息传递

yield... 和 next(...) 这一对组合起来,在生成器的执行过程中构成了一个双向消息传递系统。

next()的参数是传给生成器的,可以用来调节生成器的行为。而yield的值是返回给next()的调用者的。两者的区别如下:

next()参数 : 调用者 ---> 生成器
yield 表达式:生成器 ---> 调用者

next()方法带一个参数,该参数就会被当作上一个yield表达式的返回值。例子如下:

function* main(x) {
  const y = x * (yield 12)
  return y
}
const it = main(6) // step1
console.log(it.next()) // step 2
console.log(it.next(7)) // step3

step1: 向生成器中传入参数 6 ,则 向赋值为 6

step2: value 为 yield 后的值 value 为 12

step3: next() 向生成器中传入值 7,所以(yield 12)整体被看作 7 ,y 为 42

结果运行如下:

说明:

(1)第一个 next 是启动生成器的,没有上一个yield,所以第一个next传参无效,该参数会被忽略。

(2)如果不带参数,则表示上一个参数返回undefined

3. Generator、Promise 实现完美异步

当请求一个接口时:

<script>
  function getData () {
    return fetch('https://www.fastmock.site/mock/e9463cacabbb7690593cdbaf53f1480d/clean/test/a')
  }
  function* dataCallBack() {
    try {
      let result = yield getData()
    } catch(err) {
      console.log(err)
    }
  }
  let it = dataCallBack()
  // {value: Promise, done: false}
  let promise = it.next().value
  promise.then((res) => {console.log(res)})
</script>

当请求多个接口时:

<script>
    // generator + Promise 实现 await/async 

   // 1. Promise
    function tick (time) {
      return new Promise ((resolve, reject) => {
        setTimeout(() => {
          resolve('当前时间:' + new Date())
        }, time)
      })
    }

    // 2. generator
    function* gene() {
      let result1 = yield tick(3000)
      console.log('result1', result1)
      let result2 = yield tick(2000)
      console.log('result2', result2)
    }
    // 驱动函数
    let run = (generator, res) => {
      let start = generator.next(res)
      if (start.done) return
      start.value.then((res) => {
        run(generator, res) // 回调
      })
    }
    // 执行
    run(gene())

(二)async 是什么

根据 MDN 定义,async 是一个通过异步执行并隐式返回 Promise 作为结果的函数。

<script>
    async function a () {
      return 3
    }
    console.log(a()) // Promise {<resolved>: 3}
  </script>

async 返回一个Promise

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

知识点:Promise  立即执行 1 输出在3 前面

总结:Promise 的编程模型依然充斥着大量的 then 方法,虽然解决了回调地狱的问题,但是在语义方面依然存在缺陷,代码中充斥着大量的 then 函数,这就是 async/await 出现的原因。

1. generator 函数是如何暂停执行程序的?
答案是通过协程来控制程序执行。
generator 函数是一个生成器,执行它会返回一个迭代器,这个迭代器同时也是一个协程。一个线程中可以有多个协程,但是同时只能有一个协程在执行。线程的执行是在内核态,是由操作系统来控制;协程的执行是在用户态,是完全由程序来进行控制,通过调用生成器的next()方法可以让该协程执行,通过yield关键字可以让该协程暂停,交出主线程控制权,通过return 关键字可以让该协程结束。协程切换是在用户态执行,而线程切换时需要从用户态切换到内核态,在内核态进行调度,协程相对于线程来说更加轻量、高效。
2. async function实现原理?
async function 是通过 promise + generator 来实现的。generator 是通过协程来控制程序调度的。
​在协程中执行异步任务时,先用promise封装该异步任务,如果异步任务完成,会将其结果放入微任务队列中,然后通过yield 让出主线程执行权,继续执行主线程js,主线程js执行完毕后,会去扫描微任务队列,如果有任务则取出任务进行执行,这时通过调用迭代器的next(result)方法,并传入任务执行结果result,将主线程执行权转交给该协程继续执行,并且将result赋值给yield 表达式左边的变量,从而以同步的方式实现了异步编程。
所以说到底async function 还是通过协程+微任务+浏览器事件循环机制来实现的。

参考文章:https://www.jianshu.com/p/83da0901166f

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值