JS 异步编程都有哪些方案

  先一起来回想一下,我们在日常开发中都用过哪些 JS异步编程的方式?总结起来无外乎有这几种:回调函数、事件监听、PromiseGeneratorasync/await,这几种 JS 的编程方式都是异步编程。回调函数方式是最早的 JS 异步编程的方式,后随着ES 标准的发展,PromiseGeneratorasync/await 接连出现。

  那么在开始前先回想一下:

  1. 同步编程和异步编程的区别在哪里?
  2. 回调地狱有哪些方法可以解决?

什么是同步?

  所谓的同步就是在执行某段代码时,在该代码没有得到返回结果之前,其他代码暂时是无法执行的,但是一旦执行完成拿到返回值之后,就可以执行其他代码了。换句话说,在此段代码执行完未返回结果之前,会阻塞之后的代码执行,这样的情况称为同步。

什么是异步?

  所谓异步就是当某一代码执行异步过程调用发出后,这段代码不会立刻得到返回结果。而是在异步调用发出之后,一般通过回调函数处理这个调用之后拿到结果。异步调用发出后,不会影响阻塞后面的代码执行,这样的情形称为异步。

JS 编程中为什么需要异步?

  我们都知道 JavaScript是单线程的,如果 JS 都是同步代码执行意味着什么呢?这样可能会造成阻塞,如果当前我们有一段代码需要执行时,如果使用同步的方式,那么就会阻塞后面的代码执行;而如果使用异步则不会阻塞,我们不需要等待异步代码执行的返回结果,可以继续执行该异步任务之后的代码逻辑。因此在 JS 编程中,会大量使用异步来进行编程。

JS 异步编程方式发展历程

回调函数

  早些年为了实现 JS的异步编程,一般都采用回调函数的方式,比如比较典型的事件的回调,或者用 setTimeout/ setInterval来实现一些异步编程的操作,但是使用回调函数来实现存在一个很常见的问题,那就是回调地狱。

  这里列举了一种现实开发中会遇到的场景,来看一下代码。

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) {//....});});});
});

  从上面的代码可以看出,其逻辑为先读取 A文本内容,再根据 A文本内容读取B,然后再根据 B 的内容读取 C。为了实现这个业务逻辑,上面实现的代码就很容易形成回调地狱。回调实现异步编程的场景也有很多,比如:

  1. ajax请求的回调
  2. 定时器中的回调
  3. 事件回调
  4. Nodejs中的一些方法回调

  异步回调如果层级很少,可读性和代码的维护性暂时还是可以接受,一旦层级变多就会陷入回调地狱,上面这些异步编程的场景都会涉及回调地狱的问题。下面来看一下针对上面这个业务场景,改成 Promise来实现异步编程,会是什么样子的呢?

Promise

  为了解决回调地狱的问题,之后社区提出了 Promise的解决方案,ES6 又将其写进了语言标准,采用 Promise的实现方式在一定程度上解决了回调地狱的问题。

  还是针对上面的这个场景来看下先读取 A 文本内容,再根据 A文本内容读取B 文件,接着再根据 B文件的内容读取 C文件。我们看这样的实现通过 Promise改造之后是什么样的,请看代码。

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也存在一些问题,即便是使用 Promise的链式调用,如果操作过多,其实并没有从根本上解决回调地狱的问题,只是换了一种写法,可读性虽然有所提升,但是依旧很难维护。不过 Promise又提供了一个 all方法,对于这个业务场景的代码,用 all来实现可能效果会更好。

function read(url) {return new Promise((resolve, reject) => {fs.readFile(url, 'utf8', (err, data) => {if(err) reject(err);resolve(data);});});
}// 通过 Promise.all 可以实现多个异步并行执行,同一时刻获取最终结果的问题Promise.all([read(A), read(B), read(C)]).then(data => {console.log(data);
}).catch(err => console.log(err)
);

Generator

  Generator 也是一种异步编程解决方案,它最大的特点就是可以交出函数的执行权,Generator函数可以看出是异步任务的容器,需要暂停的地方,都用 yield语法来标注。Generator函数一般配合 yield使用,Generator函数最后返回的是迭代器。如果对迭代器不太了解,可以自行补习一下这部分内容。

  下面我们来看一下 Generator的简单使用,请看这段代码。

function* gen() {const a = yield 111;console.log(a);const b = yield 222;console.log(b);const c = yield 333;console.log(c);const d = yield 444;console.log(d);}const t = gen();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虽然执行了但是并未输出结果,后面的每次执行 next会把参数传入然后打印出来,等到最后一次 next对应的 yield执行完之后,控制台会打印 “{value: undefined, done: true}”的输出结果,标识该 Generator函数已经执行完毕,即 done:true

async/await

  ES6 之后 ES7中又提出了新的异步解决方案:async/awaitasyncGenerator 函数的语法糖,async/await的优点是代码清晰(不像使用 Promise的时候需要写很多 then的方法链),可以处理回调地狱的问题。async/await写起来使得 JS的异步代码看起来像同步代码,其实异步编程发展的目标就是让异步逻辑的代码看起来像同步一样容易理解。

  来看下 async/await的基本用法,代码如下所示。

const testWait = ()=>{return new Promise((resolve,reject)=>{setTimeout(()=>{console.log('testWait');resolve();},1000);})
}const testWaitUse = async ()=>{await testWait();console.log('mark');return 123;
}
/* 输出顺序:testWaitmark
*/
console.log(testWaitUse());

  执行上面的代码,从结果中可以看出,在正常的执行顺序下,testWait这个函数由于使用的是 setTimeout的定时器,回调会在一秒之后执行,但是由于执行到这里采用了 await关键词,testAwaitUse函数在执行的过程中需要等待 testWait函数执行完成之后,再执行打印 hello的操作。但是如果去掉 await,打印结果的顺序就会变化。

  因此,async/await不仅仅是 JS的异步编程的一种方式,其可读性也接近于同步代码,让人更容易理解。

总结

  把JS的异步编程方式回顾了一遍。希望通过这几个方式的讲解,能够对 JS 异步编程形成一个全局的认识。

JS异步编程方式 简单总结
回调函数 早些年JS异步编程采用的方式
Promise ES6新增加异步编程方式,解决回调地狱的问题
Generator yield配合使用,返回的时迭代器
async/await 二者配合使用,async返回的是Promsie对象,await控制执行顺序

JS 异步编程都有哪些方案相关推荐

  1. JS 异步编程都有哪些方案?

    什么是同步? 所谓的同步就是在执行某段代码时,在该代码没有得到返回结果之前,其他代码暂时是无法执行的,但是一旦执行完成拿到返回值之后,就可以执行其他代码了.换句话说,在此段代码执行完未返回结果之前,会 ...

  2. JS 异步编程六种方案

    前言 我们知道Javascript语言的执行环境是"单线程".也就是指一次只能完成一件任务.如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务. 这种模式虽然实现起来 ...

  3. JS 异步编程方法:6种方案

    前言: javascript语言的执行环境是"单线程".也就是指一次只能完成一件任务.如果有多个任务,就必须排队,前面一个任务完成,在执行后面一个任务 这种模式虽然实现起来比较简单 ...

  4. js 异步执行_JS Asynchronous — JS 异步编程极简史

    Asynchronous JS 异步编程极简史,这个故事网上已经很多人有了自己的讲述. Event Loop 解释了 Node.js 为何以及如何实现单线程服务模型和 Event Loop.对于 JS ...

  5. 【学习笔记】Part1·JavaScript·深度剖析-函数式编程与 JS 异步编程、手写 Promise(二、JavaScript 异步编程)

    [学习笔记]Part1·JavaScript·深度剖析-函数式编程与 JS 异步编程.手写 Promise(课前准备) [学习笔记]Part1·JavaScript·深度剖析-函数式编程与 JS 异步 ...

  6. JS异步编程的解决方案

    js解决异步编程有6种方案: 1.1 回调函数 异步编程的最基本方法,把任务的第二段单独写在一个函数里面,等到重新执行这个任务的时候,就直接调用这个函数. 优点:简单.容易理解和实现. 缺点:多次调用 ...

  7. promise 浏览器实现的源码_【大前端01-01】函数式编程与JS异步编程、手写Promise...

    [简答题]一.谈谈你是如何理解JS异步编程的,EventLoop.消息队列都是做什么的,什么是宏任务.什么是微任务? 如何理解JS异步编程 众所周知JavaScript语言执行环境是"单线程 ...

  8. JS 异步编程及常考面试题

    JS 异步编程及常考面试题 并发(concurrency)和并行(parallelism)区别 涉及面试题:并发与并行的区别? 异步和这小节的知识点其实并不是一个概念,但是这两个名词确实是很多人都常会 ...

  9. JS 异步编程的 5 种解决方案

    我们知道 JS 语言的执行环境是"单线程",所谓"单线程",就是指一次只能完成一件任务,这种模式的好处是实现起来比较简单,执行环境相对单纯:坏处是只要有一个任务 ...

最新文章

  1. 扩增子文献笔记1白杨内生和根际微生物组在不同生态位存在特异的群落结构
  2. 高德地图小蓝点_一会晴天一会下雨?夏日想要顺利出行 高德地图这些小功能最实用...
  3. SQL语句行列转换两种方法 case ...when 和pivot函数应用
  4. 江苏小米授权维修商网点收藏
  5. 二值神经网络(Binary Neural Networks)最新综述
  6. memcpy和memmove的区别以及内存重叠问题
  7. 定时执行sql统计数据库连接数并记录到表中
  8. (转载) linux安装JDK
  9. java编写个倒计时_怎么编写一个倒计时java程序?求具体步骤!
  10. GB2312-80 汉字机内码
  11. Java的11个关键术语
  12. 路由器、交换机、网关
  13. 富文本 和 图片裁切
  14. php报错_STORAGE_WRITE_ERROR_:./Application/Runtime/Cache/Admin/df12aa1edf6tt330187a6514aae4fda4.php
  15. 自媒体短视频快速吸引粉丝的诀窍
  16. 给Android虚拟机AVD加一个虚拟内存卡
  17. java jtextarea 事件_JTextArea的事件处理2
  18. scrum认证费用_如何获得专业Scrum大师的认证-快速和慢速方式
  19. 无法找到模块“vue-awesome-swiper/dist/ssr”的声明文件
  20. 矩阵压缩降维动态规划递推【P1719 最大加权矩形】

热门文章

  1. Linux host命令
  2. 玩的就是“心”跳 - Athlon超频手记 (转)
  3. Microsoft office2019下载安装步骤图文教程
  4. Android 如何在第三方app 调起qq好友和qq群
  5. 12306购票送温暖
  6. 安卓安装charles证书到系统证书
  7. 编写一个方法,去掉数组中重复元素
  8. linux文件的类型有哪些?
  9. linux namp使用教程,nmap教程之nmap命令使用示例(nmap使用方法)
  10. css 文本超出2行显示点点点