JavaScript异步发展史

一道面试题引发的血战。
问:怎么理解异步的发展过程,例如axios、ajax、promise、await、async、generator等?

  1. 为什么需要异步?

    • JavaScript是单线程语言,同一时间只能做一件事情;

    • 同步任务进入主线程,按顺序执行,意味着阻塞,前一段代码必须执行完成了才能执行后边代码;

    • 异步是不会造成阻塞的。异步任务进入任务队列,只有“任务队列”通知主线程,某个异步任务可以执行了,该任务才会进入主线程。比如ajax、setTimeout/setInterval等。上述过程不断循环,称为事件循环(EventLoop);

    • 事件循环是js实现异步的一种方式,也是js的执行机制 。

      题外话:
      1. 除了同步、异步任务,对任务有更精细的定义:宏任务[整体代码的script/setTimeout/setInterval]、微任务[Promise/process.nextTick];
      2. js的执行和运行有很大区别,js在不同环境下,比如node、浏览器等,执行方式(即执行机制,事件循环eventLoop)是不同的。而运行是指js解析引擎大多是统一的。
      3. 关于事件循环机制,详细请参考

  2. 发展过程

    • 异步最早的解决方案是回调函数,比如:
      1.setTimeout/setInterval
      2 ajax
      3 nodeAPI
      4 事件回调
      例如这样一个需求:先读取文件A,再根据文件A的内容去读取文件B,然后根据文件B的内容读取文件C...

      let fs = require('fs');
      fs.readFile('1.txt', 'utf-8', function(err, data) {
          if (err) {
              console.log(err);
          } else {
              console.log(data);
          }
      });
      
      // 回调地狱
      fs.readFile(A, function (err, data) {
          fs.readFile(B, function (err, data) {
              fs.readFile(C, function (err, data) {
                  // ...
              });
          });            
      });
      

      优点是简单;缺点是回调嵌套难以维护,不方便统一处理错误,不能try catch错误以及回调地狱。

    • Promise一定程度上解决了“回调地狱”和统一处理错误问题。
      好像Promise.all()方法也能实现,我们来看一看:

      Promise.all([
          readfilePromise('A'),
          readfilePromise('B'),
          readfilePromise('C'),
      ])
          .then((dataA, dataB, dataC) => {
              // ...
          })
          .catch(err => console.log(err));
      

      实际上这个方法的含义是等A、B、C三个文件全部读取完成后,进行操作,三个文件是独立的,不是上述栗子中的依赖关系。详细请参考MDN

      // Promise解决“回调地狱”以及统一处理错误问题
      let readfilePromise = function (filepath) {
          return new Promise(function (resolve, reject) {
              // console.log(a);
              fs.readFile(filepath, function(err, data) {
                  if (err) {
                      reject(err);
                  } else {
                      resolve(data);
                  }
              });
          });
      }
      readfilePromise('A')
          .then(data => {
              return readfilePromise('B');
          })
          .then(data => {
              return readfilePromise('C');
          })
          .catch(err => console.log(err)); // 该catch可以统一处理其前边所有错误,包括Promise内部的语法错误、异步reject、then里的错误等,且不会影响到Promise外部的代码
      
      

      好像解决了一些问题,但一定程度上又存在新问题:
      1.错误不能被try catch,只有通过catch回调捕获;
      2.使用promise的链式回调没从根本上解决回调地狱问题,它是一种新的写法,而不是新的语法,比如后一个请求依赖于前一个的结果,仍然需要嵌套多个promise实例或者then(...);

    • Generator函数是es6提供的一种新的异步编程解决方案,遇到yield命令就暂停,等到执行权返回,再从暂停的地方继续向后执行。缺点是需要结合co函数,否则不能自动执行;

    • async/await是generator的语法糖,内置执行器,使得异步代码看起来像同步代码,可以try catch错误;

      async function readdependantFile () {
          try {
              let dataA = await readfilePromise('A');
              let dataB = await readfilePromise('B');
              let dataC = await readfilePromise('C');
          } catch (err) {
              console.log(err);
          }
      }
      readdependantFile();
      
  3. 总结:异步编程的目标是让异步逻辑的代码看起来像同步一样。

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

参考《ECMAScript6 入门》 阮一峰《细说JS异步发展历程》 刘小夕@segmentfault

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,874评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,102评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,676评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,911评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,937评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,935评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,860评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,660评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,113评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,363评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,506评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,238评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,861评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,486评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,674评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,513评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,426评论 2 352

推荐阅读更多精彩内容

  • js中的异步是指一个函数在执行过程中,其中一部分不能马上执行完毕,然后执行函数体中另外一部分。等到第一部分得到返回...
    柳贤_e8c5阅读 297评论 0 0
  • 回调函数假定有两个函数f1和f2,后者等待前者的执行结果。f1(); f2();如果f1是一个很耗时的任务,可以考...
    五秋木阅读 296评论 0 0
  • 弄懂js异步 讲异步之前,我们必须掌握一个基础知识-event-loop。 我们知道JavaScript的一大特点...
    DCbryant阅读 2,710评论 0 5
  • 首先这篇文章是参考阮一峰老师的ES6章节中的generator部分写的,再加上我自己的理解,新手如果要学习ES6推...
    c菜鸟阅读 550评论 0 0
  • 1、谈判原则 谈判原则是永远不接受首次报价,而且在开始和对手谈判时,我开的条件一定是大大高出对方期望。因为开的条件...
    日更飘阅读 201评论 0 0