面试官问 async、await 函数原理是在问什么?

大家好,我是若川。这是 源码共读活动《1个月,200+人,一起读了4周源码》 第四期,纪年小姐姐的第四次投稿。纪年小姐姐通过本次学习提早接触到generator,协程概念,了解了async/await函数的原理等。

第四期是 学习 koa 源码的整体架构,浅析koa洋葱模型原理和co原理中的co原理。不知不觉,源码共读已经进行了一个月,有些小伙伴表示对面试和工作很有帮助,学完立马能用。如果你也感兴趣可以加我微信 ruochuan12参加。


1. 前言

这周看的是 co 的源码,我对 co 比较陌生,没有了解和使用过。因此在看源码之前,我希望能大概了解 co 是什么,解决了什么问题。

2. 简单了解 co

先看了 co 的 GitHub,README 是这样介绍的:

Generator based control flow goodness for nodejs and the browser, using promises, letting you write non-blocking code in a nice-ish way.

看起来有点懵逼,又查了一些资料,大多说 co 是用于 generator 函数的自动执行。generator 是 ES6 提供的一种异步编程解决方案,它最大的特点是可以控制函数的执行。

2.1 关于 generator

说到异步编程,我们很容易想到还有 promise,async 和 await。它们有什么区别呢?先看看 JS 异步编程进化史:callback -> promise -> generator -> async + await

JS 异步编程

再看看它们语法上的差异:

CallbackPromiseGeneratorasync + await + Promise
ajax(url, () => {})Promise((resolve,reject) => { resolve() }).then()function* gen() { yield 1}async getData() {  await fetchData() }

关于 generator 的学习不在此篇幅详写了,需要了解它的概念和语法。

3. 学习目标

经过简单学习,大概明白了 co 产生的背景,因为 generator 函数不会自动执行,需要手动调用它的 next() 函数,co 的作用就是自动执行 generator 的 next() 函数,直到 done 的状态变成 true 为止。

那么我这一期的学习目标:

1)解读 co 源码,理解它是如何实现自动执行 generator

2)动手实现一个简略版的 co

4. 解读 co 源码

co 源码地址:https://github.com/tj/co

4.1 整体架构

从 README 中,可以看到是如何使用 co :

co(function* () {var result = yield Promise.resolve(true);return result;
}).then(function (value) {console.log(value);
}, function (err) {console.error(err.stack);
});

从代码可以看到它接收了一个 generator 函数,返回了一个 Promise,这部分对应的源码如下。

function co(gen) {var ctx = this;// 获取参数var args = slice.call(arguments, 1);// 返回一个 Promisereturn new Promise(function(resolve, reject) {// 把 ctx 和参数传递给 gen 函数if (typeof gen === 'function') gen = gen.apply(ctx, args);// 判断 gen.next 是否函数,如果不是直接 resolve(gen)if (!gen || typeof gen.next !== 'function') return resolve(gen);// 先执行一次 nextonFulfilled();// 实际上就是执行 gen.next 函数,获取 gen 的值function onFulfilled(res) {var ret;try {ret = gen.next(res);} catch (e) {return reject(e);}next(ret);return null;}// 对 gen.throw 的处理function onRejected(err) {var ret;try {ret = gen.throw(err);} catch (e) {return reject(e);}next(ret);}// 实际处理的函数,会递归执行,直到 ret.done 状态为 truefunction next(ret) {// 如果生成器的状态 done 为 true,就 resolve(ret.value),返回结果if (ret.done) return resolve(ret.value);// 否则,将 gen 的结果 value 封装成 Promisevar value = toPromise.call(ctx, ret.value);// 判断 value 是否 Promise,如果是就返回 thenif (value && isPromise(value)) return value.then(onFulfilled, onRejected);// 如果不是 Promise,Rejectedreturn onRejected(new TypeError('You may only yield a function, promise, generator, array, or object, '+ 'but the following object was passed: "' + String(ret.value) + '"'));}});
}

看到这里,我产生了一个疑问:Promise + then 也可以处理异步编程,为什么 co 的源码里要把 Promise + generator 结合起来呢,为什么要这样做?直到我搞懂了 co 的核心目的,它使 generator 和 yield 的语法更趋向于同步编程的写法,引用阮一峰的网络日志中的一句话就是:

异步编程的语法目标,就是怎样让它更像同步编程。

可以看一个 Promise + then 的例子:

function getData() {return new Promise(function(resolve, reject) {resolve(1111)})
}
getData().then(function(res) {// 处理第一个异步的结果console.log(res);// 返回第二个异步return Promise.resolve(2222)
})
.then(function(res) {// 处理第二个异步的结果console.log(res)
})
.catch(function(err) {console.error(err);
})

如果有多个异步处理就会需要写多少个 then 来处理异步之间可能存在的同步关系,从以上的代码可以看到 then 的处理是一层一层的嵌套。如果换成 co,在写法上更优雅也更符合日常同步编程的写法:

co(function* () {try {var result1 = yield Promise.resolve(1111)// 处理第一个异步的结果console.log(result1);// 返回第二个异步var result2 = yield Promise.resolve(2222)// 处理第二个异步的结果console.log(result2)} catch (err) {console.error(err)}
});

4.2 分析 next 函数

源码的 next 函数接收一个 gen.next() 返回的对象 ret 作为参数,形如{value: T, done: boolean},next 函数只有四行代码。

第一行:if (ret.done) return resolve(ret.value); 如果 ret.done 为 true,表明 gen 函数到了结束状态,就 resolve(ret.value),返回结果。

第二行:var value = toPromise.call(ctx, ret.value); 调用 toPromise.call(ctx, ret.value) 函数,toPromise 函数的作用是把 ret.value 转化成 Promise 类型,也就是用 Promise 包裹一层再 return 出去。

function toPromise(obj) {// 如果 obj 不存在,直接返回 objif (!obj) return obj;// 如果 obj 是 Promise 类型,直接返回 objif (isPromise(obj)) return obj;// 如果 obj 是生成器函数或遍历器对象, 就递归调用 co 函数if (isGeneratorFunction(obj) || isGenerator(obj)) return co.call(this, obj);// 如果 obj 是普通的函数类型,转换成 Promise 类型函数再返回if ('function' == typeof obj) return thunkToPromise.call(this, obj);// 如果 obj 是一个数组, 转换成 Promise 数组再返回if (Array.isArray(obj)) return arrayToPromise.call(this, obj);// 如果 obj 是一个对象, 转换成 Promise 对象再返回if (isObject(obj)) return objectToPromise.call(this, obj);// 其他情况直接返回return obj;
}

第三行:if (value && isPromise(value)) return value.then(onFulfilled, onRejected); 如果 value 是 Promise 类型,调用 onFulfilled 或 onRejected,实际上是递归调用了 next 函数本身,直到 done 状态为 true 或 throw error。

第四行:return onRejected(...) 如果不是 Promise,直接 Rejected。

5. 实践

虽然解读了 co 的核心代码,看起来像是懂了,实际上很容易遗忘。为了加深理解,结合上面的 co 源码和自己的思路动手实现一个简略版的 co。

5.1 模拟请求

function request() {return new Promise((resolve) => {setTimeout(() => {resolve({data: 'request'});}, 1000);});
}
// 用 yield 获取 request 的值
function* getData() {yield request()
}
var g = getData()
var {value, done} = g.next()
// 间隔1s后打印 {data: "request"}
value.then(res => console.log(res))

5.2 模拟实现简版 co

核心实现:

1)函数传参

2)generator.next 自动执行

function co(gen) {// 1. 传参var ctx = this;const args = Array.prototype.slice.call(arguments, 1);gen = gen.apply(ctx, args);return new Promise(function(resolve, reject) {// 2. 自动执行 nextonFulfilled()function onFulfilled (res) {var ret = gen.next(res);next(ret);}function next(ret){if (ret.done) return resolve(ret.value);// 此处只处理 ret.value 是 Promise 对象的情况,其他类型简略版没处理var promise = ret.value;// 自动执行promise && promise.then(onFulfilled);}})
}// 执行
co(function* getData() {var result = yield request();// 1s后打印 {data: "request"}console.log(result)
})

6. 感想

对我来说,学习一个新的东西(generator)花费的时间远远大于单纯阅读源码的时间,因为需要了解它产生的背景,语法,解决的问题以及一些应用场景,这样在阅读源码的时候才知道它为什么要这样写。

读完源码,我们会发现,其实 co 就是一个自动执行 next() 的函数,而且到最后我们会发现 co 的写法和我们日常使用的 async/await 的写法非常相像,因此也不难理解【async/await 实际上是对 generator 封装的一个语法糖】这句话了。

// co 写法
co(function* getData() {var result = yield request();// 1s后打印 {data: "request"}console.log(result)
})
// async await 写法
(async function getData() {var result = await request();// 1s后打印 {data: "request"}console.log(result)
})()

不得不说,阅读源码的确是一个开阔视野的好方法,如果不是这次活动,我可能还要晚个大半年才接触到 generator,接触协程的概念,了解到 async/await 实现的原理,希望能够继续坚持下去~

最近组建了一个江西人的前端交流群,如果你是江西人可以加我微信 ruochuan12 私信 江西 拉你进群。


推荐阅读

1个月,200+人,一起读了4周源码
我读源码的经历

老姚浅谈:怎么学JavaScript?

我在阿里招前端,该怎么帮你(可进面试群)

················· 若川简介 ·················

你好,我是若川,毕业于江西高校。现在是一名前端开发“工程师”。写有《学习源码整体架构系列》多篇,在知乎、掘金收获超百万阅读。
从2014年起,每年都会写一篇年度总结,已经写了7篇,点击查看年度总结。
同时,活跃在知乎@若川,掘金@若川。致力于分享前端开发经验,愿景:帮助5年内前端人走向前列。

识别方二维码加我微信、拉你进源码共读

今日话题

略。欢迎分享、收藏、点赞、在看我的公众号文章~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/275463.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一步步优化JVM六:优化吞吐量[转]

2019独角兽企业重金招聘Python工程师标准>>> 原文:http://ganlv.iteye.com/blog/1571315 参考:http://www.myexception.cn/software-architecture-design/1455594.html 现代JVM是一个具有灵活适应各种应用能力的软件,尽管很多应用…

element-ui 网格_UI备忘单:列表与网格

element-ui 网格重点 (Top highlight)Grids or lists? That is the question we will look at in this cheat sheet. While they can be used anywhere in your site, we are going to look primarily at search results, catalogs and newsfeeds. Making this choice will de…

50行 koa-compose,面试常考的中间件原理原来这么简单?

大家好,我是若川。源码共读《1个月,200人,一起读了4周源码》 活动第五期是学习 koa 源码的整体架构,浅析koa洋葱模型原理和co原理中的koa-compose源码原理,阅读不到50行的koa-compose源码。这篇是izjing小哥哥的投稿。…

sqlite3源码编译到Android,实现SQLite跨全平台使用

文/何晓杰Dev(高级Android架构师)著作权归作者所有,转载请联系作者获得授权。初看这个标题你可能会不解,SQLite 本身就是一个跨平台的数据库,在这里再说跨平台有什么意义呢?其实不然,目前我就遇到了一个项目…

illustrator下载_平面设计:16个Illustrator快捷方式可加快工作流程

illustrator下载I know, I know — keyboard shortcuts sound so nerdy, and you’re a graphic designer, not an IT Director, why should you learn keyboard shortcuts?我知道,我知道—键盘快捷键听起来很书呆,而且您是图形设计师,而不是…

手把手教你五分钟扒个源码写个无敌外挂

大家好,我是若川。源码共读《1个月,200人,一起读了4周源码》 活动进行到第五期了,欢迎点链接加我微信 ruochuan12 报名参加。前言前段时间群里分享了一个小游戏,多次怀疑自己的眼睛以后,尝试去写个外挂。中…

Kubernetes 1.14重磅来袭,多项关键特性生产可用

走过了突飞猛进的2018年,Kubernetes在2019年终于迎来了第一个大动作:Kubernetes 1.14版本的正式发布!Kubernetes 本次发布的 1.14 版本,包含了 31 项增强,其中 10 项为 GA,12 项进入 beta 试用阶段&#xf…

open ai gpt_让我们来谈谈将GPT-3 AI推文震撼到核心的那条推文

open ai gpt重点 (Top highlight)“设计师”插件 (The ‘Designer’ plugin) A couple days ago, a tweet shared by Jordan Singer turned the heads of thousands of designers. With the capabilities of GPT-3 (from OpenAI), he shared a sample of what he was able to c…

我历时3年才写了10余篇源码文章,但收获了100w+阅读

你好,我是若川。最近来了一些读者朋友,在这里简单介绍自己的经历,也许对你有些启发。之前发过这篇文章,现在修改下声明原创,方便保护版权。最近组织了源码共读活动1个月,200人,一起读了4周源码&…

第 8 章 容器网络 - 061 - flannel 的连通与隔离

flannel 的连通与隔离 测试 bbox1 和 bbxo2 的连通性: bbox1 能够 ping 到位于不同 subnet 的 bbox2,通过 traceroute 分析一下 bbox1 到 bbox2 的路径。 1) bbox1 与 bbox2 不是一个 subnet,数据包发送给默认网关 10.2.9.1&#…

计算机视觉笔记本推荐_视觉灵感:Mishti笔记本

计算机视觉笔记本推荐The Mishti Notebook is a project close to my heart, wherein I experimented with screen printing techniques at the Print Labs at the National Institute of Design, Ahmedabad. Dating back to the year 2012 when the NID Print Labs was first …

Google工程师:如何看待程序员普遍缺乏数据结构和算法知识?

出处:极客时间《数据结构与算法之美》很多技术人都很迷茫,觉得自己做的项目没有技术含量,成天就是卖苦力。技术的东西,日新月异,有些人总在忙于追求热点新技术,东学学、西学学,平时泛泛地看技术…

android guide 中文版,Sky Guide

Sky Guide是一款能让小伙伴们观察银河的手机软件,尤其是喜欢行星、星座的小伙伴们来讲,这款软件能很好的帮助小伙伴们观看这些,让小伙伴们体验不一样的观星乐趣,因此想要观看的小伙伴们,赶紧来试试吧。软件介绍&#x…

layui选项卡嵌套选项卡_在ProtoPie中使用嵌套组件构建选项卡栏

layui选项卡嵌套选项卡One of the powerful features of ProtoPie is the ability to build fully portable and interactive UI components. We are going to make use of nested components, SVG icons, and layout constraints to build a tab bar UI component that is sel…

50行代码串行Promise,koa洋葱模型原来这么有趣?

1. 前言大家好,我是若川,最近组织了源码共读活动《1个月,200人,一起读了4周源码》,感兴趣的可以加我微信 ruochuan12 参与,长期交流学习。之前写的《学习源码整体架构系列》 包含jQuery、underscore、lodas…

js 用迭代器模式优雅的处理递归问题

2019独角兽企业重金招聘Python工程师标准>>> 什么是迭代器 循环数组或对象内每一项值,在 js 里原生已经提供了一个迭代器。 var arr [1, 2, 3] arr.forEach(function (item) {console.log(item) })实现一个迭代器 var iterator function (arr, cb) {fo…

如何抓取html请求,请求获取网页的response,获取网页的html 怎么那么慢

HttpEntity multipart builder.build();httppost.setEntity(multipart);long start System.currentTimeMillis();// 发送请求response httpclient.execute(httppost);long end System.currentTimeMillis();System.out.println("查询upsfreight消耗的时间是(毫秒):&quo…

Serverless 究竟是什么?

大家好,我是若川。说起 Serverless,我想你应该并不陌生,作为一种云开发的架构模式,在近两年里,伴随着云原生概念的推广愈发火爆。作为一名 Serverless 的拥趸,在跟大家推荐的过程中,我经常能看到…

instagram.apk_评论:Instagram Reels vs.TikTok

instagram.apkWith all the attention to the newly debuted Instagram Reels from Facebook and the hilarious, bizarre world of TikTok, here’s a first impression on the two platforms and how they compare from a designer’s perspective.所有人都在关注Facebook新近…

240多个jQuery常用到的插件

概述 jQuery 是继 prototype 之后又一个优秀的 Javascript 框架。其宗旨是—写更少的代码,做更多的事情。它是轻量级的 js 库(压缩后只有21k) ,这是其它的 js 库所不及的,它兼容 CSS3,还兼容各种浏览器(IE 6.0, FF 1.5, Safari 2.…