切面是异步还是同步操作‘_细说JS异步发展历程

知其然知其所以然,首先了解三个概念:

1.什么是同步?

所谓同步,就是在发出一个"调用"时,在没有得到结果之前,该“调用”就不返回。但是一旦调用返回,就得到返回值了。换句话说,就是由“调用者”主动等待这个“调用”的结果。此调用执行完之前,阻塞之后的代码执行。

2.什么是异步?

"调用"在发出之后,这个调用就直接返回了,没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在"调用"发出后,"被调用者"通过状态、通知来通知调用者,或通过回调函数处理这个调用。异步调用发出后,不影响后面代码的执行。

3.JavaScript 中为什么需要异步?

首先我们知道JavaScript是单线程的(即使新增了webworker,但是本质上JS还是单线程)。同步代码意味着什么呢?意味着有可能会阻塞,当我们有一个任务需要时间较长时,如果使用同步方式,那么就会阻塞之后的代码执行。而异步则不会,我们不会等待异步代码的执行,继续执行异步任务之后的代码。

d1db008bb3fd5d185ac1f9d0372b17f0.png

概念了解完了,我们就要进入今天的正题了。首先大家思考一下:平时在工作中,主要使用了哪些异步解决方案,这些异步方案有什么优缺点?

4baa0f86a74fbf6650b238ff1c9be0cf.png

异步最早的解决方案是回调函数,如事件的回调,setInterval/setTimeout中的回调。但是回调函数有一个很常见的问题,就是回调地狱的问题(稍后会举例说明);

为了解决回调地狱的问题,社区提出了Promise解决方案,ES6将其写进了语言标准。Promise一定程度上解决了回调地狱的问题,但是Promise也存在一些问题,如错误不能被try catch,而且使用Promise的链式调用,其实并没有从根本上解决回调地狱的问题,只是换了一种写法。

ES6中引入 Generator 函数,Generator是一种异步编程解决方案,Generator 函数是协程在 ES6 的实现,最大特点就是可以交出函数的执行权,Generator 函数可以看出是异步任务的容器,需要暂停的地方,都用yield语句注明。但是 Generator 使用起来较为复杂。

ES7又提出了新的异步解决方案:async/await,async是 Generator 函数的语法糖,async/await 使得异步代码看起来像同步代码,异步编程发展的目标就是让异步逻辑的代码看起来像同步一样。

回调函数 ---> Promise ---> Generator ---> async/await.

1.callback

//node读取文件

fs.readFile(xxx, 'utf-8', function(err, data) {

//code

});

回调函数的使用场景(包括但不限于):

  1. 事件回调

  2. Node API

  3. setTimeout/setInterval中的回调函数

  4. ajax 请求

回调函数的优点: 简单。

回调函数的缺点:

异步回调嵌套会导致代码难以维护,并且不方便统一处理错误,不能 trycatch 和 回调地狱(如先读取A文本内容,再根据A文本内容读取B再根据B的内容读取C...)。

fs.readFile(A, 'utf-8', function(err, data) {

fs.readFile(B, 'utf-8', function(err, data) {

fs.readFile(C, 'utf-8', function(err, data) {

fs.readFile(D, 'utf-8', function(err, data) {

//....

});

});

});

});

2.Promise

Promise 一定程度上解决了回调地狱的问题,Promise 最早由社区提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。

那么我们看看Promise是如何解决回调地狱问题的,仍然以上文的readFile 为例(先读取A文本内容,再根据A文本内容读取B再根据B的内容读取C)。

function read(url) {

return new Promise((resolve, reject) => {

fs.readFile(url, 'utf8', (err, data) => {

if(err) reject(err);

resolve(data);

});

});

}

read(A).then(data => {

return read(B);

}).then(data => {

return read(C);

}).then(data => {

return read(D);

}).catch(reason => {

console.log(reason);

});

Promise 的优点:

  1. 一旦状态改变,就不会再变,任何时候都可以得到这个结果

  2. 可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数

缺点:

  1. 无法取消 Promise

  2. 当处于pending状态时,无法得知目前进展到哪一个阶段

  3. 错误不能被 trycatch

假设有这样一个需求:读取A,B,C三个文件内容,都读取成功后,再输出最终的结果。在Promise之前,我们一般可以借助发布订阅模式去实现:

let pubsub = {

arry: [],

emit() {

this.arry.forEach(fn => fn());

},

on(fn) {

this.arry.push(fn);

}

}

let data = [];

pubsub.on(() => {

if(data.length === 3) {

console.log(data);

}

});

fs.readFile(A, 'utf-8', (err, value) => {

data.push(value);

pubsub.emit();

});

fs.readFile(B, 'utf-8', (err, value) => {

data.push(value);

pubsub.emit();

});

fs.readFile(C, 'utf-8', (err, value) => {

data.push(value);

pubsub.emit();

});

Promise给我们提供了 Promise.all 的方法,对于这个需求,我们可以使用 Promise.all 来实现。

/**

* 将 fs.readFile 包装成promise接口

*/

function read(url) {

return new Promise((resolve, reject) => {

fs.readFile(url, 'utf8', (err, data) => {

if(err) reject(err);

resolve(data);

});

});

}

/**

* 使用 Promise

*

* 通过 Promise.all 可以实现多个异步并行执行,同一时刻获取最终结果的问题

*/

Promise.all([

read(A),

read(B),

read(C)

]).then(data => {

console.log(data);

}).catch(err => console.log(err));

可执行代码可戳: https://github.com/YvetteLau/Blog/blob/master/JS/Async/index.js

3.Generator

Generator 函数是 ES6 提供的一种异步编程解决方案,整个 Generator 函数就是一个封装的异步任务,或者说是异步任务的容器。异步操作需要暂停的地方,都用 yield 语句注明。

Generator 函数一般配合 yield 或 Promise 使用。Generator函数返回的是迭代器。对生成器和迭代器不了解的同学,请自行补习下基础。下面我们看一下 Generator 的简单使用:

function* gen() {

let a = yield 111;

console.log(a);

let b = yield 222;

console.log(b);

let c = yield 333;

console.log(c);

let d = yield 444;

console.log(d);

}

let t = gen();

//next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值

t.next(1); //第一次调用next函数时,传递的参数无效

t.next(2); //a输出2;

t.next(3); //b输出3;

t.next(4); //c输出4;

t.next(5); //d输出5;

为了让大家更好的理解上面代码是如何执行的,我画了一张图,分别对应每一次的next方法调用:

7eb71bbbdb16a40f5ac00a2323f78abc.png

仍然以上文的 readFile (先读取A文本内容,再根据A文本内容读取B再根据B的内容读取C)为例,使用 Generator + co库来实现:

const fs = require('fs');

const co = require('co');

const bluebird = require('bluebird');

const readFile = bluebird.promisify(fs.readFile);

function* read() {

yield readFile(A, 'utf-8');

yield readFile(B, 'utf-8');

yield readFile(C, 'utf-8');

//....

}

co(read()).then(data => {

//code

}).catch(err => {

//code

});

Generator的缺点大约不用我说了,除非是找虐,不然一般不会直接使用 Generator 来解决异步的(当然也不排除是因为我不熟练)~~~

不使用co库,如何实现?能否自己写一个最简的 my_co,有助于理解 async/await 的实现原理 ?请戳: https://github.com/YvetteLau/Blog/blob/master/JS/Async/generator.js

PS: 如果你还不太了解 Generator/yield,建议阅读ES6相关文档。

4.async/await

ES7中引入了 async/await 概念。async 其实是一个语法糖,它的实现就是将 Generator函数和自动执行器(co),包装在一个函数中。

async/await 的优点是代码清晰,不用像 Promise 写很多 then 链,就可以处理回调地狱的问题。并且错误可以被try catch。

仍然以上文的readFile (先读取A文本内容,再根据A文本内容读取B再根据B的内容读取C) 为例,使用 async/await 来实现:

const fs = require('fs');

const bluebird = require('bluebird');

const readFile = bluebird.promisify(fs.readFile);

async function read() {

await readFile(A, 'utf-8');

await readFile(B, 'utf-8');

await readFile(C, 'utf-8');

//code

}

read().then((data) => {

//code

}).catch(err => {

//code

});

使用 async/await 实现此需求:读取A,B,C三个文件内容,都读取成功后,再输出最终的结果。

function read(url) {

return new Promise((resolve, reject) => {

fs.readFile(url, 'utf8', (err, data) => {

if(err) reject(err);

resolve(data);

});

});

}

async function readAsync() {

let data = await Promise.all([

read(A),

read(B),

read(C)

]);

return data;

}

readAsync().then(data => {

console.log(data);

});

所以JS的异步发展史,可以认为是从 callback -> promise -> generator -> async/await。async/await 使得异步代码看起来像同步代码,异步编程发展的目标就是让异步逻辑的代码看起来像同步一样。

因本人水平有限,文中内容未必百分百正确,如有不对的地方,请给我留言,谢谢。

邀请你加入 Step-By-Step 项目

不积跬步无以至千里。 我是公众号【前端宇宙】作者刘小夕,我将和大家一起一步一个脚印,向前端专家迈进。Step-By-Step

每个工作日我会发布一个前端相关的问题(目的是为了切实掌握相关的知识点),欢迎在 Issue 区留下你的答案。

节假日不会发布任何问题,希望大家能够利用节假日回顾一周所学。每周末我会进行一次汇总(整理出最优答案),以便大家回顾。

参考文章:

[1] 细说JavaScript异步函数发展历程

[2] ES6 Promise

[3] ES6 Generator

[4] ES6 async

[5] JavaScript异步编程

谢谢各位小伙伴愿意花费宝贵的时间阅读本文,如果本文给了您一点帮助或者是启发,请不要吝啬你的赞和Star,您的肯定是我前进的最大动力。https://github.com/YvetteLau/Blog

关注小姐姐的公众号,加入交流群。

8dc1362d48abb206fc0b7d8bfd186e27.png

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

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

相关文章

数据科学家需要掌握的10项统计技术,快来测一测吧

摘要: 本文给出了数据科学应用中的十项统计学习知识点,相信会对数据科学家有一定的帮助。无论你是不是一名数据科学家,都不能忽视数据的重要性。数据科学家的职责就是分析、组织并利用这些数据。随着机器学习技术的广泛应用,深度学…

java获取mysql的自增列_java - MyBatis如何获取Mysql自增id

问 题INSERT INTO USER(name,age,address,loan_type)VALUES(#{name},#{age},#{address},#{loanType});user表中id自增,添加过一条数据后,可以得到主键id;DEBUG [main] - > Preparing: INSERT INTO USER(name,age,address,loan_type) VALUE…

hosts文件位置在哪里

C:\Windows\System32\drivers\etc\hosts

反转!2019程序员吸金榜来了,AI程序员刷爆了..

前两天在网上发现一个热门话题:“做开发一年,在北京月薪不到1万,有点迷茫。” 其中,这个回答我永远忘不了:在这短短的一条信息里,小编佩服不仅仅是毕业一年的AI程序员拿到年薪60万,而是这一番回…

前端详细设计文档怎么写_UI设计师简历应该怎么写?

像这种分享,常规开篇都应该说说当前的就业趋势啦,分析分析行业形势啦这类的但我不想按流程写行业不论什么时候分析,它都没好过,什么红利期什么风口,那更是从来没赶上过。但凡我能跟点风,我也不能到现在还没…

阿里云携领先SDN能力,亮相全球网络技术盛会ONS

摘要: 网络通讯届盛会Open Network Summit(ONS)于3月29日落下了帷幕。作为开源网络届首屈一指的世界性大会,今年在洛杉矶举办的ONSNA2018已经是第七年举办了。参会者囊括了来自电信运营商、云服务提供商、网络设备制造商、芯片厂商、网络软件开发商、研究…

容器开启数据服务之旅系列(一):Kubernetes如何解自建PostgreSQL运维之痛

摘要: 通过阿里云Kubernetes容器服务,开启你的数据服务之旅 (一)云上运维自建数据库之痛,使用容器服务自动恢复数据库postgresql实例 概述 本文为大家介绍一种容器化的数据服务 posgresql db on ACK,通过使用云盘自动挂…

今日头条技术架构分析

戳蓝字“CSDN云计算”关注我们哦!今日头条创立于2012年3月,到目前仅4年时间。从十几个工程师开始研发,到上百人,再到200余人。产品线由内涵段子,到今日头条,今日特卖,今日电影等产品线。一、产品…

mysql图书管理数据库的三个关系模式_数据库 考虑如下关于图书馆的关系模式,用关系代数写出查询(数据库系统概念第六版6.14)...

贝尔梅尔娜美2019.03.15采纳率:60% 等级:39已帮助:91565人数据库系统的基本概念数据:实际上就是描述事物的符号记录。数据的特点:有一定的结构,有型与值之分,如整型、实型、字符型等。而数据…

dom文档对象手册_HTML5学习之DOM编程

DOM是Document Object Model的缩写,中文名称是文档对象模型。DOM是处理HTML页面的标准编程接口,【前端精选40G资料包赠送co,每日新闻资讯ding,每天进步一点点bb,小写英文为微信】DOM可被JavaScript用来读取、改变HTML的内容和结构…

Kubernetes之路 2 - 利用LXCFS提升容器资源可见性

摘要: 这是本系列的第2篇内容,将介绍在Docker和Kubernetes环境中解决遗留应用无法识别容器资源限制的问题。本系列文章记录了企业客户在应用Kubernetes时的一些常见问题 第一篇:Java应用资源限制的迷思 第二篇:利用LXCFS提升容器资…

mysql数据库模型相应解释_数据库事务系列-MySQL跨行事务模型

说来和MySQL倒是有缘,毕业的第一份工作就被分配到了RDS团队,主要负责把MySQL弄到云上做成数据库服务。虽说整天和MySQL打交道,但说实话那段时间并没有很深入的理解MySQL内核,做的事情基本都是围绕着MySQL做管控系统,比…

springboot项目jar冲突问题解决

问题:大概意思就是项目中有两个jar,同时是要是想slf4j的接口的,这样程序不知道使用哪个,就会报错了。也不算错,项目照样运行。但是就是报日志错误 SLF4J: Class path contains multiple SLF4J bindings. SLF4J: Found …

火热的云计算,你知道这些吗?

戳蓝字“CSDN云计算”关注我们哦!作者 | Dan Muse译者 | 风车云马如今云计算已经渗透到IT的各个领域,从应用程序到基础设施无处不在。为了了解IT领导者是如何规划各自企业的云战略,Insider Pro采访了数百名技术决策者。面对炙手可热的云计算&…

Kubernetes Ingress 高可靠部署最佳实践

摘要: 在Kubernetes集群中,Ingress作为集群流量接入层,Ingress的高可靠性显得尤为重要,今天我们主要探讨如何部署一套高性能高可靠的Ingress接入层。 简介 在Kubernetes集群中,Ingress是授权入站连接到达集群服务的规则…

容器开启数据服务之旅系列(二):Kubernetes如何助力Spark大数据分析

摘要: 容器开启数据服务之旅系列(二):Kubernetes如何助力Spark大数据分析 (二):Kubernetes如何助力Spark大数据分析 概述 本文为大家介绍一种容器化的数据服务Spark OSS on ACK,允许…

三步走——带你打造一份完美的数据科学家简历|(附件有PPT福利)

摘要: 本文介绍了关于写数据科学家简历的一些技巧,主要包含三个部分,分别为简历前的材料准备,写简历时应注意的地方以及对整个简历的整理。不管你是不是数据科学领域的工作者,本文对于即将求职或找实习的同学而言是一份…

你的目的是什么是谁指使你_电视剧《谁说我结不了婚》第25-27集剧情:魏书帮程璐搞定投资人...

电视剧《谁说我结不了婚》第25-27集剧情介绍电视剧《谁说我结不了婚》第25-27集剧情介绍电视剧《谁说我结不了婚》第25集剧情介绍:程璐向魏书请教感情困扰 田蕾帮徐海峰彻底打垮凯文程璐来找魏书诉苦,小哈最近不但躲着她,还撒谎骗她不在上海&…

纪·阿晶的首次AWS之行!

戳蓝字“CSDN云计算”关注我们哦!这是阿晶的第一次AWS之行,在上海,2019世界人工智能大会。一年前,同样在上海,AWS成立其亚太地区首个人工智能研究院。还记得当时振奋人心的官宣:AWS上海人工智能研究院将重点…

阿里云与WPS深度合作,开放数据处理生态

摘要: 在3月28日举行的2018云栖大会-深圳峰会上,阿里云与金山办公达成深度合作,WPS在线预览与格式转换能力落地阿里云。标志着阿里云存储开放的数据湖体系不但面向计算引擎,还面向应用开放。 在3月28日举行的2018云栖大会-深圳峰会…