AsyncAwait 语法糖与Generator异步方案

本文探讨了Async/Await语法糖与Generator在异步编程中的应用。通过一个字节跳动的面试题,解释了Async函数的执行顺序。文章详细阐述了Generator函数的工作原理,展示了如何通过Generator处理多个Promise请求,避免回调地狱。最后,指出Async是基于Generator的语法糖,简化了异步编程的复杂性,并简要提及了官方在实现Async时所做的优化。
摘要由CSDN通过智能技术生成

Async/Await 语法糖与Generator异步方案

首先我们来看一下字节跳动的一道面试题:

setTimeout(function () {
    console.log('setTimeout')
}, 0)

async function async1() {
    console.log('async1 start')
    await async2()
    console.log('async1 end')
}

async function async2() {
    console.log('async2')
}

console.log('script start')

async1()

console.log('script end')

大家可以自己思考一下这道题的答案,然后去 nodejs 跑一下这段代码或者直接看我跑的

在这里插入图片描述

相信大家都和实际运行结果差不多,但是倒数第二条和倒数第三条的顺序反了,我一开始想的也是先打印 async1 end 再打印 script end,直到我去扒开 async 的语法糖,才知道了 async 的正确运行顺序。

在这里插入图片描述

我们应该不难理解,前两个输出先是 script start 和 async1 start,因为 setTimeout 是在“主线程”全部走完之后才会执行的,所以它的输出必定在 script end 之后。接着,由于 async1 的调用,代码执行第六行,输出 async1 start。后面,就到了扒开糖衣的时候了。

generator 异步方案

function* foo() {
    console.log('test1')
    //暂停执行并向外返回值
    var s = yield 'next return data' //调用next后,返回对象值
    // 注意,上面代码分两次next
    console.log(s)

    console.log('end code')
}
// 调用函数,不会立即执行,返回生成器对象
const gener = foo()

// 调用next方法,才会开始执行
// 返回包含 yield 内容的对象
const yields = gener.next()

console.log(yields) // () => {value:"xxxx", done: false}
// 对象中done表示生成器是否已经执行完毕
// 对象中value是函数中yield的返回值
// done:false 表示函数中的代码并没有执行完毕

//下一次 next 方法的调用,会从前面函数 yield 后的代码开始执行
// next() 方法的传参,是函数中 yield 的返回值

generator 是一种语法规则,其内容是在 function 后面或者函数名前面加个 * 号,并且函数中带有 yield 关键字,当首次调用这个函数的时候,函数不执行,只有调用函数的 next 方法的时候,函数开始执行,但是只会执行到 yield 后面的部分(前面赋值不执行),也就是返回 ‘next return data’ ,第二次调用next方法时,执行 yield 前面的赋值(赋的值是 next 传入的参数)操作和后面的代码。

function* fun(){
    var b = yield ajax('http://localhost:8080') // ajax为我自己封装的ajax请求,返回一个promise对象
    console.log(b)
    var c = yield ajax('http://localhost:8080')
    console.log(c)
}

const gener = fun()

var back1 = gener.next()

back1.value.then(backDate => {
    var back2 = gener.next(backDate)
    back2.value.then(backDate => {
        gener.next(backDate)
    })
})

我们可以像上面一样使用 generator 函数,可是这又有什么优势呢?当我们存在需要多次 next 的时候,这时候我们回调函数式有规律的,这时候我们只要将回调地狱换成以下代码即可

function h(res) {
    if(res.done) return
    res.value.then(backData => {
        h(gener.next(backData))
    })
}

h(back1)

讲到这里,我就可以告诉大家 async 就是根据这个逻辑进行封装的,我们只要将 * 换成 async,将 yield 换成 awiat,就是我们现在使用的语法糖了。当然也不只是替换那么简单,官方的在封装的过程中还是进行了很多优化 的,这里我就不细讲了,只要懂了这个逻辑,相信大家对 async 异步编程是怎么进行的都有了大概的了解了吧。

最后再讲一下开头的代码,

在这里插入图片描述

当代码执行到 awiat 处开启异步,等待 await 的结果,所以打印 async2,接着阻塞异步的代码,开始同步的操作,即执行19行代码输出 script end,同步操作执行完毕后才会回到异步的代码继续执行打印 async1 end,最后等所有同步异步结束后,开始执行定时器代码打印 setTimeout。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值