深刻理解async/await

<pre>

一、前言

我最初接触 async/await 是在完成一个小demo,在koa的教程里初次见到 async/await (其实也是第一次接触ES6),以下是文档中的一个小demo中的一段:

// x-response-time

app.use(async function (ctx, next) {
  const start = new Date();
  await next();
  const ms = new Date() - start;
  ctx.set('X-Response-Time', `${ms}ms`);
});

起初还不理解这个await是什么意思,实验了一下发现这个 await 会将代码执行的权利交给他后面的函数,等到后面这个函数执行完后再执行之后的代码。

当时我觉得这个功能可叼了,即使我当时连 JS 是单线程的都不知道,但我还是觉得这个功能可叼了。

活学活用,用着可爽了,但是后来逐渐发现不对劲了——好像 await 也不是啥都等,当时做百度前端学院作业时,我想执行一个child_process,但是我发现我 await exec(....)他无论如何都不等exec执行完后才继续往下执行。在网上也找了很多资料,我发现居然还有个东西叫Promise,说要在await后面放Promise才行什么的,我擦,真的不知道还有这种东西。最后搞半天也没搞出来,草草收场。(其实主要还是因为自己啥也不懂)

二、引导小练习

其实我当时要解决的一个需求很简单,我有一个异步操作,我希望程序等我这个一步操作执行完了再继续向下执行(虽然这个需求很奇异,但是这也让我对async/await有更深入理解有很大的帮助),举个栗子:

我现在想要如下代码输出 1 2 3


console.log(1);

setTimeout(function () {
  console.log(2);
}, 1000);

console.log(3);

该怎么修改呢?

当初我的想法就是

// 注意,这是错误的做法

(async function () {

  console.log(1);

  await setTimeout(function () {
    console.log(2);
  }, 1000);

  console.log(3);

}())

你可以在控制台输出看看,你会发现输出的是1 3 2,而且中间还会穿插一个Promise(这个不是你的代码输出的,是浏览器自带魔法)。

其实正确的做法应该是这样的


(async function () {

  console.log(1);

  await new Promise(function (resolve, reject) {
    "use strict";
    setTimeout(function () {
      console.log(2);
      resolve();
    }, 1000);
  });

  console.log(3);

}())

写得更加fashion一些:


(async function () {

  console.log(1);

  await new Promise(resolve => {
    "use strict";
    setTimeout(() => {
      console.log(2);
      resolve();
    }, 1000);
  });

  console.log(3);

}())

对,要加一个小小的new Promise(...)

这是为什么呢?

三、async/await

1. async 函数

我们先看MDN上关于async function怎么说的:

When an async function is called, it returns a Promise. When the async function returns a value, the Promise will be resolved with the returned value. When the async function throws an exception or some value, the Promise will be rejected with the thrown value.

也就是说async函数会返回一个Promise对象。

  • 如果async函数中是return一个值,这个值就是Promise对象中resolve的值;

  • 如果async函数中是throw一个值,这个值就是Promise对象中reject的值。

举个代码:

async函数的写法

async function imAsync(num) {
  if (num > 0) {
    return num // 这里相当于resolve(num)
  } else {
    throw num // 这里相当于reject(num)
  }
}

imAsync(1).then(function (v) {
  console.log(v); // 1
});

// 注意这里是catch
imAsync(0).catch(function (v) {
  console.log(v); // 0
})

Promise的写法

function imPromise(num) {

  return new Promise(function (resolve, reject) {
    if (num > 0) {
      resolve(num);
    } else {
      reject(num);
    }
  })
}

imPromise(1).then(function (v) {
  console.log(v); // 1
})

imPromise(0).then(function (v) {
  console.log(v); // 0
})

2. await

再来看看MDN对于await是怎么说的:

An async function can contain an await expression, that pauses the execution of the async function and watis for the passed Promise's resolution, and then resumes the async function's execution and returns the resolved value.

“await会暂停当前async函数的执行,等待后面的Promise的计算结果返回以后再继续执行当前的async函数。”

所以我们之前如果单纯的 await setTimeout(...) 或者 await exec(...) 是不行滴,await 不是什么都等,它等待的只是Promise,你如果没有给他返回个Promise,那它还是会继续向下执行。

所以 await 等待的不是所有的异步操作,等待的只是Promise。

3. async/await 诞生的初衷(是要解决什么问题?)

MDN上还说了一句:

The purpose of async/await functions is to simplify the behavior of using promises synchronously and to perform some behavior on a group of Promises. Just like Promises are similar to structured callbacks, async/await is similar to combining generators and promises.

“async/await是为了简化多个Promise的同步操作,就像Promise要解决层层嵌套的回调函数的问题一样

所以async/await就是要完善Promise还不够完美的地方,是在Promise的基础上进行改进的,因此也很好理解为什么await只能“等待”Promise对象。

四、个人对async/await的理解

以下是个人理解的async/await,如果有说的不对的地方,你来打我啊!

async/await是在Promise之后产生的,它和Promise诞生的目的都是为了解决“回调地狱”,至于什么是回调地狱:

[图片上传失败...(image-7da36f-1516934967467)]

Promise改进后:

[图片上传失败...(image-dd2971-1516934967467)]

async/await改进后:

[图片上传失败...(image-f17c45-1516934967467)]

图片来自阮一峰的微博

很牛逼吧!

所以我发现async/await是在Promise的基础上做了改进,await是接收一个Promise对象,而当Promise执行到resolve()或者reject()的时候(fulfilled和rejected),await才会继续往下执行。

所以关键点就是得是返回Promise对象的函数才行,不然await等你后面的函数执行完了,见你没返回Promise对象,那他就继续执行了,不管你了。

所以我们也能看出,Promise和async/await的应用场景是大量连续的异步操作,像我刚刚举的小栗子的这种需求其实也不常见哈。

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

推荐阅读更多精彩内容