ES6 Promise 和 async/await 的使用

你可能知道,Javascript语言的执行环境是"单线程"(single thread)。

所谓"单线程",就是指一次只能完成一件任务。如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,以此类推。

这种模式的好处是实现起来比较简单,执行环境相对单纯;坏处是只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。常见的浏览器无响应(假死),往往就是因为某一段Javascript代码长时间运行(比如死循环),导致整个页面卡在这个地方,其他任务无法执行。

为了解决这个问题,Javascript语言将任务的执行模式分成两种:同步(Synchronous)和异步(Asynchronous)。

1.回调

回调是异步编程最基本的方法。

假定有两个函数f1和f2,后者等待前者的执行结果。

f1();
f2();

如果f1是一个很耗时的任务,可以考虑改写f1,把f2写成f1的回调函数。

function f1(callback){
  setTimeout(function () {
    // f1的任务代码
    callback();
  }, 1000);
}

执行代码就变成下面这样

f1(f2);

采用这种方式,我们把同步操作变成了异步操作,f1不会堵塞程序运行,相当于先执行程序的主要逻辑,将耗时的操作推迟执行。回调函数的优点是简单、容易理解和部署,缺点是不利于代码的阅读和维护,各个部分之间高度耦合,流程会很混乱,而且每个任务只能指定一个回调函数。

2.Promise

Promises对象是CommonJS工作组提出的一种规范,目的是为异步编程提供统一接口。

简单说,它的思想是,每一个异步任务返回一个Promise对象,该对象有一个then方法,允许指定回调函数。Promises的出现大大改善了异步变成的困境,避免出现回调地狱,嵌套层级得到改善。

基本Api

Promise.resolve()
Promise.reject()
Promise.prototype.then()
Promise.prototype.catch()
Promise.all()  // 所有的完成
Promise.race() // 竞速,完成一个即可

具体api的介绍请看 阮一峰 大神的 ECMAScript 6 入门
在这我举几个简单的场景的实现

模拟两个异步请求

为了使代码简介,Promiserejected状态的相关reject()catch()方法省略

  // 1请求
  function getData1 () {
    return new Promise(function (resolve, reject) {
      setTimeout(() => {
        console.log('1执行了')
        resolve('请求到模拟数据1111拉')
      }, 2000)
    })
  }
  // 2请求
  function getData2 (params) {
    return new Promise(function (resolve, reject) {
      setTimeout(() => {
        console.log('2执行了')
        resolve('请求到模拟数据22222拉!params:' + params)
      }, 1500)
    })
  }

promise 实现异步回调 异步列队

1请求完成后,把1的响应参数传入2,在发2请求

  function promiseDemo () {
    getData1()
      .then(res => {
        return getData2(res)
      })
      .then(res => {
        console.log(res)
      })
  }
  promiseDemo()
  // 1执行了
  // 2执行了
  // 请求到模拟数据22222拉!params:请求到模拟数据1111拉   用时 3500 ms

promise.all() 实现异步回调 并发 所有的完成

1请求、2请求同时发,两条响应都收到后再执行

  function promiseDemo () {
    Promise.all([getData1(), getData2()]).then(function (res) {
      console.log(res)
    })
  }
  // 2执行了
  // 1执行了
  // ["请求到模拟数据1111拉", "请求到模拟数据22222拉!params:undefined"]   用时 2000 ms

promise.race() 实现异步回调 并发 竞速

1请求、2请求同时发,其中一条收到请求就执行

  function promiseDemo () {
    Promise.race([getData1(), getData2()]).then(function (res) {
      console.log(res)
    })
  }
  // 2执行了
  // 请求到模拟数据22222拉!params:undefined    用时 1500 ms
  // 1执行了

由此Promise对象还是很好用的,对于异步的流程的控制得到了大大改善,通过.then()的方法可进行链式调用。可是.then() .catch()使用也导致代码非常难看,嵌套也很深,所以async/await就出来了

3.Async/await

async/await 是 Javascript 编写异步程序的新方法。以往的异步方法无外乎回调函数和Promise。但是async/await建立于Promise之上。

如何使用 Async 函数

我们还是来看一看 阮一峰 大神的ECMAScript 6 入门的例子

async function timeout(ms) {
  await new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}

async function asyncPrint(value, ms) {
  await timeout(ms);
  console.log(value);
}

asyncPrint('hello world', 50);

上面代码指定50毫秒以后,输出hello world。
进一步说,async函数完全可以看作多个异步操作,包装成的一个 Promise 对象,而await命令就是内部then命令的语法糖

我们看具体的示例

async 实现异步回调 异步列队

1请求完成后,把1的响应参数传入2,在发2请求

上文中的promise 实现方法是通过then的链式调用,但是采用async会更加简洁明了

  async function asyncDemo () {
    const r1 = await getData1()
    const r2 = await getData2(r1)
    console.log(r2)
  }
  // 1执行了
  // 2执行了
  // 请求到模拟数据22222拉!params:请求到模拟数据1111拉   用时 3500 ms

用同步的书写方式实现了异步的代码。等待getData1的异步函数执行完了后发返回值赋值给r1,传入r2,在执行r2

async 异步回调 并发

1请求、2请求同时发,规定请求到达的顺序

假如我们有一种这样的业务需求,并发两个请求,但是要规定收到请求的顺序应该怎么做的?这里还是借鉴阮一峰大神的代码

  async function asyncDemo2 () {
    const arr = [getData1, getData2]
    const textPromises = arr.map(async function (doc) {
      const response = await doc()
      return response
    })
    // 按次序输出
    for (const textPromise of textPromises) {
      console.log(await textPromise);
    }
  }
  // 2执行了            (因为2是 1500ms后执行) 所以2先执行
  // 1执行了
  // 请求到模拟数据1拉  (for .. of )规定了输出的顺序
  // 请求到模拟数据22222拉!params:undefined

上面代码中,虽然map方法的参数是async函数,但它是并发执行的,因为只有async函数内部是继发执行,外部不受影响。后面的for..of循环内部使用了await,因此实现了按顺序输出

async 总结

它使得异步代码变的不再明显也是一点弊端咯,不过根据实际情况选择最合适的异步编程才是最好的选择。async 是 Generator 函数的语法糖。所以想更深入的理解其中内部原理的赶紧去看看 Generator函数把

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

推荐阅读更多精彩内容