异步无处不在:终极解决方案(四)

勾勾的前端世界

共 2809字,需浏览 6分钟

 ·

2021-01-13 22:00

(。・∀・)ノ゙嗨,我是你稳定更新、持续输出的勾勾。



异步无处不在:同步模式和异步模式(一)

异步无处不在:回调函数(二)

异步无处不在:Promise 破除“回调地狱”(三)


前三篇内容,让我们认识了 JS 世界中的同步和异步模式,也清楚了回调地狱的产生和破解。


回忆一下第三篇的结尾:


虽然我们脱离了回调地狱,但是 .then 的链式调用依然不太友好。


频繁的 .then 并不符合自然的运行逻辑,Promise 的写法只是回调函数的改进,使用then 方法以后,异步任务的两段执行看得更清楚了,除此以外,并无新意。


Promise 的最大问题是代码冗余。原来的任务被 Promise 包装了一下,不管什么操作,一眼看去都是一堆 then,原来的语义变得很不清楚。 


于是,在 Promise 的基础上,Async 函数出现了。


终极异步解决方案,

千呼万唤地在 ES2017 中发布了。


Async/Await 语法糖


Async 函数使用起来,也是很简单。


将调用异步的逻辑全部写进一个函数中,函数前面使用 async 关键字。


在函数中异步调用逻辑的前面使用 await ,异步调用会在 await 的地方等待结果,然后进入下一行代码的执行,这就保证了代码的后续逻辑,可以等待异步的 ajax 调用结果了。


而代码看起来的执行逻辑,和同步代码几乎一样。


async function callAjax(){     var a = await myAjax('./d1.json')     console.log(a);     var b = await myAjax('./d2.json');     console.log(b)     var c = await myAjax('./d3.json');     console.log(c) }callAjax();


注意:await 关键词只能在 async 函数内部使用。


因为使用简单,很多人也不会探究其使用的原理,无非就是两个单词加到前面用就好了。虽然会用,日常开发看起来也没什么问题,但是一遇到 Bug 调试,就只能凉凉。面试的时候也总是知其然不知其所以然。


之前也写过一篇 BAT 的异步面试真题:字节百度前端面试真题:异步处理方案(内附答案)。有兴趣的小伙伴可以再次回顾下。


咱们先来一个面试题试试,你能运行出正确的结果吗?


async 面试题


请写出以下代码的运行结果:

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')


答案我放在最后面,你也可以自己写出来运行一下。


想要把结果搞清楚,我们需要引入另一个内容:Generator 生成器函数


先看一段代码:

function * foo(){    console.log('test');    // 暂停执行并向外返回值     yield 'yyy'; // 调用 next 后,返回对象值    console.log(33);}
// 调用函数 不会立即执行,返回 生成器对象const generator = foo();
// 调用 next 方法,才会 *开始* 执行 // 返回 包含 yield 内容的对象 const yieldData = generator.next();
console.log(yieldData) //=> {value: "yyy", done: false}// 对象中 done ,表示生成器是否已经执行完毕// 函数中的代码并没有执行结束
// 下一次的 next 方法调用,会从前面函数的 yeild 后的代码开始执行console.log(generator.next()); //=> {value: undefined, done: true}


你会发现,在函数声明的地方,函数名前面多了 * 星号,函数体中的代码有个 yield ,用于函数执行的暂停。


简单点说就是,这个函数不是个普通函数,调用后不会立即执行全部代码,而是在执行到 yield 的地方暂停函数的执行,并给调用者返回一个遍历器对象。


yield 后面的数据,就是遍历器对象的 value 属性值。


如果要继续执行后面的代码,需要使用遍历器对象中的 next() 方法,代码会从上一次暂停的地方继续往下执行。


是不是 so easy!


同时,在调用 next 的时候,还可以传递参数,函数中上一次停止的 yeild 就会接受到当前传入的参数。


function * foo(){    console.log('test');    // 下次 next 调用传参接受    const res = yield 'yyy';     console.log(res);}
const generator = foo();
// next 传值 const yieldData = generator.next();console.log(yieldData)
// 下次 next 调用传参,可以在 yield 接受返回值generator.next('test123');


Generator 的最大特点就是让函数的运行可以暂停


不要小看他,有了这个暂停,我们能做的事情就太多了。在调用异步代码时,就可以先 yield 停一下,停下来我们就可以等待异步的结果了。


那么如何把 Generator 写到异步中呢?

明天见(ง •_•)ง。


推荐阅读:

异步无处不在:同步模式和异步模式(一)

异步无处不在:回调函数(二)

异步无处不在:Promise 破除“回调地狱”(三)

Redux 续集 | 如何搞定其中的异步操作?

技术人年度总结 | 2020,注定不平凡

2020 最后一篇技术文:可爱的乌咪 UmiJS

是我 Web 端配不上阿里了。

不可避免的问题:React 的路由如何抽离?

前端人因为 Vue3 的 Ref-sugar 提案打起来了!


点点“”和“在看”,保护头发,减少bug。

浏览 22
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报