Javascript中的async awai的用法


Posted in Javascript onMay 17, 2017

async / await是ES7的重要特性之一,也是目前社区里公认的优秀异步解决方案。目前,async / await这个特性已经是stage 3的建议,可以看看TC39的进度,本篇文章将分享async / await是如何工作的,阅读本文前,希望你具备Promise、generator、yield等ES6的相关知识。

在详细介绍async / await之前,先回顾下目前在ES6中比较好的异步处理办法。下面的例子中数据请求用Node.js中的request模块,数据接口采用Github v3 api文档提供的repo代码仓库详情API作为例子演示。

Promise对异步的处理

虽然Node.js的异步IO带来了对高并发的良好支持,同时也让“回调”成为灾难,很容易造成回调地狱。传统的方式比如使用具名函数,虽然可以减少嵌套的层数,让代码看起来比较清晰。但是会造成比较差的编码和调试体验,你需要经常使用用ctrl + f去寻找某个具名函数的定义,这使得IDE窗口经常上下来回跳动。使用Promise之后,可以很好的减少嵌套的层数。另外Promise的实现采用了状态机,在函数里面可以很好的通过resolve和reject进行流程控制,你可以按照顺序链式的去执行一系列代码逻辑了。下面是使用Promise的一个例子:

const request = require('request');
// 请求的url和header
const options = {
 url: 'https://api.github.com/repos/cpselvis/zhihu-crawler',
 headers: {
  'User-Agent': 'request'
 }
};
// 获取仓库信息
const getRepoData = () => {
 return new Promise((resolve, reject) => {
  request(options, (err, res, body) => {
   if (err) {
    reject(err);
   }
   resolve(body);
  });
 });
};

getRepoData()
 .then((result) => console.log(result);)
 .catch((reason) => console.error(reason););

// 此处如果是多个Promise顺序执行的话,如下:
// 每个then里面去执行下一个promise
// getRepoData()
//  .then((value2) => {return promise2})
//  .then((value3) => {return promise3})
//  .then((x) => console.log(x))

不过Promise仍然存在缺陷,它只是减少了嵌套,并不能完全消除嵌套。举个例子,对于多个promise串行执行的情况,第一个promise的逻辑执行完之后,我们需要在它的then函数里面去执行第二个promise,这个时候会产生一层嵌套。另外,采用Promise的代码看起来依然是异步的,如果写的代码如果能够变成同步该多好啊!

Generator对异步的处理

谈到generator,你应该不会对它感到陌生。在Node.js中对于回调的处理,我们经常用的TJ / Co就是使用generator结合promise来实现的,co是coroutine的简称,借鉴于python、lua等语言中的协程。它可以将异步的代码逻辑写成同步的方式,这使得代码的阅读和组织变得更加清晰,也便于调试。

const co = require('co');
const request = require('request');

const options = {
 url: 'https://api.github.com/repos/cpselvis/zhihu-crawler',
 headers: {
  'User-Agent': 'request'
 }
};
// yield后面是一个生成器 generator
const getRepoData = function* () {
 return new Promise((resolve, reject) => {
  request(options, (err, res, body) => {
   if (err) {
    reject(err);
   }
   resolve(body);
  });
 });
};

co(function* () {
 const result = yield getRepoData;
 // ... 如果有多个异步流程,可以放在这里,比如
 // const r1 = yield getR1;
 // const r2 = yield getR2;
 // const r3 = yield getR3;
 // 每个yield相当于暂停,执行yield之后会等待它后面的generator返回值之后再执行后面其它的yield逻辑。
 return result;
}).then(function (value) {
 console.log(value);
}, function (err) {
 console.error(err.stack);
});

async / await对异步的处理

虽然co是社区里面的优秀异步解决方案,但是并不是语言标准,只是一个过渡方案。ES7语言层面提供async / await去解决语言层面的难题。目前async / await 在 IE edge中已经可以直接使用了,但是chrome和Node.js还没有支持。幸运的是,babel已经支持async的transform了,所以我们使用的时候引入babel就行。在开始之前我们需要引入以下的package,preset-stage-3里就有我们需要的async/await的编译文件。

无论是在Browser还是Node.js端都需要安装下面的包。

$ npm install babel-core --save
$ npm install babel-preset-es2015 --save
$ npm install babel-preset-stage-3 --save

这里推荐使用babel官方提供的require hook方法。就是通过require进来后,接下来的文件进行require的时候都会经过Babel的处理。因为我们知道CommonJs是同步的模块依赖,所以也是可行的方法。这个时候,需要编写两个文件,一个是启动的js文件,另外一个是真正执行程序的js文件。

启动文件index.js

require('babel-core/register');
require('./async.js');

真正执行程序的async.js

const request = require('request');

const options = {
 url: 'https://api.github.com/repos/cpselvis/zhihu-crawler',
 headers: {
  'User-Agent': 'request'
 }
};

const getRepoData = () => {
 return new Promise((resolve, reject) => {
  request(options, (err, res, body) => {
   if (err) {
    reject(err);
   }
   resolve(body);
  });
 });
};

async function asyncFun() {
 try {
  const value = await getRepoData();
  // ... 和上面的yield类似,如果有多个异步流程,可以放在这里,比如
  // const r1 = await getR1();
  // const r2 = await getR2();
  // const r3 = await getR3();
  // 每个await相当于暂停,执行await之后会等待它后面的函数(不是generator)返回值之后再执行后面其它的await逻辑。
  return value;
 } catch (err) {
  console.log(err);
 }
}

asyncFun().then(x => console.log(`x: ${x}`)).catch(err => console.error(err));

注意点:

  1. async用来申明里面包裹的内容可以进行同步的方式执行,await则是进行执行顺序控制,每次执行一个await,程序都会暂停等待await返回值,然后再执行之后的await。
  2. await后面调用的函数需要返回一个promise,另外这个函数是一个普通的函数即可,而不是generator。
  3. await只能用在async函数之中,用在普通函数中会报错。
  4. await命令后面的 Promise 对象,运行结果可能是 rejected,所以最好把 await 命令放在 try...catch 代码块中。

其实,async / await的用法和co差不多,await和yield都是表示暂停,外面包裹一层async 或者 co来表示里面的代码可以采用同步的方式进行处理。不过async / await里面的await后面跟着的函数不需要额外处理,co是需要将它写成一个generator的。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
jQuery库与其他JS库冲突的解决办法
Feb 07 Javascript
写给想学习Javascript的朋友一点学习经验小结
Nov 23 Javascript
JQUERY实现左侧TIPS滑进滑出效果示例
Jun 27 Javascript
基于jquery实现简单的分页控件
Mar 17 Javascript
工作中常用的js、jquery自定义扩展函数代码片段汇总
Dec 22 Javascript
JavaScript实现职责链模式概述
Jan 25 Javascript
Vue-cli项目获取本地json文件数据的实例
Mar 07 Javascript
vue + element-ui的分页问题实现
Dec 17 Javascript
js中Generator函数的深入讲解
Apr 07 Javascript
配置node服务器并且链接微信公众号接口配置步骤详解
Jun 21 Javascript
微信小程序 自定义复选框实现代码实例
Sep 04 Javascript
微信小程序单选框自定义赋值
May 26 Javascript
ES6入门教程之Iterator与for...of循环详解
May 17 #Javascript
angular.fromJson与toJson方法用法示例
May 17 #Javascript
Node.js编写CLI的实例详解
May 17 #Javascript
ES6入门教程之Class和Module详解
May 17 #Javascript
AngularJS实现动态添加Option的方法
May 17 #Javascript
AngularJS+bootstrap实现动态选择商品功能示例
May 17 #Javascript
Vue声明式渲染详解
May 17 #Javascript
You might like
使PHP自定义函数返回多个值
2006/11/26 PHP
php图片的二进制转换实现方法
2014/12/15 PHP
详解php框架Yaf路由重写
2017/06/20 PHP
关于php支持的协议与封装协议总结(推荐)
2017/11/17 PHP
jquery插件制作 手风琴Panel效果实现
2012/08/17 Javascript
jquery必须知道的一些常用特效方法及使用示例(整理)
2013/06/24 Javascript
jQuery事件绑定on()、bind()与delegate() 方法详解
2015/06/03 Javascript
EasyUi combotree 实现动态加载树节点
2016/04/01 Javascript
Vue2.0表单校验组件vee-validate的使用详解
2017/05/02 Javascript
深入理解vue.js中的v-if和v-show
2017/06/22 Javascript
微信小程序获取微信运动步数的实例代码
2017/07/20 Javascript
vue使用$emit时,父组件无法监听到子组件的事件实例
2018/02/26 Javascript
vue 组件 全局注册和局部注册的实现
2018/02/28 Javascript
微信小程序仿朋友圈发布动态功能
2018/07/15 Javascript
Vue的H5页面唤起支付宝支付功能
2019/04/18 Javascript
使用JS实现动态时钟
2020/03/12 Javascript
微信小程序实现抖音播放效果的实例代码
2020/04/11 Javascript
JQuery使用数组遍历跳出each循环
2020/09/01 jQuery
[00:10]DOTA2全国高校联赛 以DOTA2会友
2018/05/30 DOTA
[02:10]DOTA2 TI10勇士令状玩法及不朽Ⅰ展示:焕新世界,如你所期
2020/05/29 DOTA
Python的批量远程管理和部署工具Fabric用法实例
2015/01/23 Python
让python 3支持mysqldb的解决方法
2017/02/14 Python
Python之多线程爬虫抓取网页图片的示例代码
2018/01/10 Python
Tensorflow 合并通道及加载子模型的方法
2018/07/26 Python
Python中创建二维数组
2018/10/17 Python
解决python3 HTMLTestRunner测试报告中文乱码的问题
2018/12/17 Python
Python如何使用Gitlab API实现批量的合并分支
2019/11/27 Python
python爬虫学习笔记之Beautifulsoup模块用法详解
2020/04/09 Python
Python开发入门——迭代的基本使用
2020/09/03 Python
selenium设置浏览器为headless无头模式(Chrome和Firefox)
2021/01/08 Python
享受加州生活方式的时尚舒适:XCVI
2018/07/09 全球购物
外国语学院毕业生自荐信
2013/10/28 职场文书
2014年骨干教师工作总结
2014/12/19 职场文书
2016大一新生军训心得体会
2016/01/11 职场文书
Win10防火墙白名单怎么设置?Win10添加防火墙白名单方法
2022/04/06 数码科技
详解CSS3浏览器兼容
2022/12/24 HTML / CSS