async 和 await

这段时间一直在重构项目,遇见很多请求“高并发”,因为涉及到多个请求又或者多个连续请求。之所以给高并发带上引号,因为面对大量请求的时候,我们需要调整好姿势,怎样去好好的去写异步回调,弄清各个请求的顺序,稍微不注意可能就掉坑了,可能调试半天看着vue-devtool控制台打印的自己以为的“完美的数据”,但是页面各种显示不听话。哦,那说明你掉坑了!
理清好执行的先后顺序,其次再写回调的时候我们就要选好正确的方法和正确的姿势,这样才不会造成将来你写的代码你认不清的尴尬,同时感觉代码一目了然!
同时考虑到异步回调我们需要理解一些知识:
js的运行机制:
在代码运行时会形成任务队列,分为同步任务队列和异步任务对列,同步队列优先加载,异步任务队列会形成队列任务池,定时器不会一下加载到异步任务,而是在设定的时间后加载到异步任务,即使设置为0,浏览器也有它的响应时间,以前是10ms.现在是4ms.异步任务包括dom 事件,定时器,promise
首先作为异步回调的功能和作用不去作过多解释,对于js这种单线程异步回调是性能优化的一些点。
异步回调我觉得主要有两方面作用:

  • 不阻碍程序运行,将一些延时较久的函数异步执行,不妨碍正常同步运行的代码;
  • 一个函数必须在某个函数执行完成后才能运行,比如说需要用函数执行完后的某些数据;
    首先写异步回调的姿势大概有这样几种
  1. 直接函数套函数(通俗的讲)
    就像这样
function fn(callback) {
  setTimeout(() => {
    callback()
  }, 1000);
}
function f1() {
  console.log('f1')
}
fn(f1)
// fn
// f1

这样我们感觉还行,还不错,还能接受,那如果有另外一个f2函数,f1像fn那样,在多个f3:

function fn(callback) {
  setTimeout(() => {
    callback(f2)
  }, 1000);
  console.log('fn')
}
function f1(callback) {
  setTimeout(() => {
    callback(f3)
  }, 1000);
  console.log('f1')
}
function f2(callback) {
  setTimeout(() => {
    callback(f3)
  }, 1000);
  console.log('f2')
}
function f3() {
  console.log('f3');
}
fn(f1)
// fn
// f1
// f2
//f3

这样看着代码也没这么乱,但是感觉把自己调懵了,如果想看出它的一些过程,我们将函数改写一下

function fn(callback) {
  setTimeout(() => {
    callback((callback) => {
      setTimeout(() => {
        callback()
      }, 1000);
      console.log('f2')
    })
  }, 1000);
  console.log('fn')
}
fn((callback) => {
  setTimeout(() => {
    callback((callback) => {
      console.log('f3')
    })
  }, 1000);
  console.log('f1')
})
// fn
// f1
// f2
// f3

如果连起来看,分清之前的f1,f2,f3函数很困难吧,这还只是三个回调函数,有时候不仅仅这些吧,如果再来两个,咋样???
所以传统方法晦涩难懂。。。。

  1. 第二种方法看起来可能就比较爽了------Promise
    Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件,更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。
    所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理
    前两句话是摘自阮一峰老师的《es6入门》
    我理解的Promise其实就是给你封装好的一个异步对象,本身有resolve和reject参数,当然也是函数,其实这里叫函数也不太好,我们不如说叫“自定义钩子”,当然这个还不像我们常说的钩子函数那样。Promise对象外部暴露的了两个分别对应的是成功之后的then函数,另一个是reject函数。说白了这两个函数是由用来设计resolve和reject函数的。Promise只负责将你写的函数在内部调用,同时将他的返回值有两个(这里说的两个是两种结果)传递出来,成功之后自然就是resolve(data),失败是reject(data),当然我们写resolve的代码块其实就是Promise实例执行完后对应的then函数的执行,当然data就是then函数回调的参数,当然reject和catch()也是一样的道理。其实看着这么像函数传参的过程,也这么像“依赖注入”的赶脚。
    不说别的了,直接将上面函数改写一下:
function fn() {
  console.log('fn');
  return new Promise(resolve => {
    setTimeout(() => {
      resolve()
    }, 1000);
  })
}
function f1() {
  console.log('f1')
  return new Promise(resolve => {
    setTimeout(() => {
      resolve()
    }, 1000);
  })
}
function f2() {
  console.log('f2')
  return new Promise(resolve2 => {
    setTimeout(() => {
      resolve2()
    }, 1000);
  })
}
function f3() {
  console.log('f3');
}
fn().then(() => {
  f1().then(() => {
    f2().then(() => {
      f3()
    })
  })
})
// fn
// f1
// f2
// f3

等到讲aync和await时再好好讲讲Promise,今天的项目有一个特点就被埋坑了
有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易,看着上面的代码是不是清爽多了,但是感觉函数一多结构看着似乎也不那么友好,同时Promise也有弊端就是获取错误信息的时候,这些就不赘述了,网上应该有很多介绍的,今天的主角是async 和 await

  1. async 和 await
    这两个方法其实就是Generator 函数的语法糖。详情请见阮一峰的es6
    先说说背景,今天做一个黑名单的需求:
    拉取软件列表的时候,首先我需要对list中存在的黑名单来个请求黑名单的请求,同时请求完成后我们需要将黑名单list存储在vue的data对象里,然后再通过对list进行过滤,对是黑名单的item进行软件图标的置灰操作,同时在不同的分类我们还需要进行判断是否进行过黑名单的操作,若果进行过就可以对本分组进行锁定和解锁操作,同时后续操作我们还需要拉取一个已经锁定分组的集合,同时是判定当前分组是否属于已经锁定分组,相应的在页面中对当前分组是显示解锁按钮还是锁定按钮
    鉴于涉及到这么多的请求,一个请求完成后需要完成几个请求才能进行相应参数,同时需要请求的先后顺序很明确。
    刚开始考虑就用Promise,因为封装的ajax方法就是基于axios的,但涉及到这么多连续请求先别说结构不友好,可能出错你都不知道怎么出的,所以我打算用async和await:
    当然咱们还是先把上面的那个例子改一下再说项目中的问题:
function f1() {
  console.log('f1')
  return new Promise(resolve => {
    setTimeout(() => {
      resolve()
    }, 1000);
  })
}
function f2() {
  console.log('f2')
  return new Promise(resolve2 => {
    setTimeout(() => {
      resolve2()
    }, 1000);
  })
}
function f3() {
  console.log('f3');
}
async function fn() {
  console.log('fn');
  await f1()
  await f2()
  f3()
}
fn()
// fn
// f1
// f2
// f3

这样写代码是不是看起来清爽多了!
今天写逻辑的时候犯了一个影响智商的错看代码和执行效果
在公司写的代码不变贴上来,所以就来模拟一下函数的执行

  async function fn() {
    await f1()
    console.log('fn')
  }
  function f1() {
    new Promise(resolve => {
      setTimeout(() => {
        console.log('f1')
        resolve()
      }, 1000);
    })
  }
  fn()
// fn
// f1
async function fn() {
    await f1()
    console.log('fn')
  }
  function f1() {
   return new Promise(resolve => {
      setTimeout(() => {
        console.log('f1')
        resolve()
      }, 1000);
    })
  }
  fn()
// f1
// fn

看看两次执行的结果
第一个函数块写的时候完全不按异步执行去操作,总是像一般函数那样,await当成了异步队列,我只想说明注意的一个点事用Promise时的return。
我们应该了解下面这些事:
Promise有一个性质就是立即执行,我们需要将它外包一层函数(就叫fn),当我们await fn(),请记住fn只是为了不让promise立即执行,所以我们一定得在fn函数中返回promise,如果不返回,应该知道没有返回值的函数的执行结果是undefined,还执行个毛线呀!
记住函数没有返回值执行结果就是undefined!!!!
同时还有一个比较常见的问题:

async getList() {
        fetchList(this.listQuery).then(
          ({data}) => {
          let list = data.list
          this.softTotalNum = data.total
          // 获取黑名单list
          await this.getBlackList()
          list.map(item => {
            if (this.soft_ids.indexOf(item.soft_id) >= 0) {
              item['is_hidden'] = 1
            }
            return item
          })
          this.list = list;
        })
      },
这时候报的错

其实应该这样写

getList() {
        fetchList(this.listQuery).then(
         async ({data}) => {
          let list = data.list
          this.softTotalNum = data.total
          // 获取黑名单list
          await this.getBlackList()
          list.map(item => {
            if (this.soft_ids.indexOf(item.soft_id) >= 0) {
              item['is_hidden'] = 1
            }
            return item
          })
          this.list = list;
        })
      },

一定要在你用的await的最近的父级用async声明异步函数,其他的顶级父级没有必要写async

今天遇到坑后总结了下面三点:

  1. async是一个异步函数声明词,await必须在async函数中使用,await后面应该你用一个延时函数,当然你用一个一般函数也行,就是立即执行而已。根据单词的字面意思,await我们可以理解为必须等我后面的函数执行完之后,下面的代码才能运行。
  2. await的最近父级必须是async函数,否则会报“await is a reserved word”错。
  3. await对应的如果类似于promise的函数,鉴于promise的立即执行的特点,我们需要将它外包一层函数(就叫fn),当我们await fn(),请记住fn只是问了不让promise立即执行,所以我们一定得在fn函数中返回promise,如果不返回,应该知道没有返回值的函数的执行结果是undefined,还执行个毛线呀!
    以上就是一些按async和await的一些知识,当然这是es7的东西,记得babel编译!!!
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335