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 相关文章推荐
innerhtml用法 innertext用法 以及innerHTML与innertext的区别
Oct 26 Javascript
Jquery Ajax Error 调试错误的技巧
Nov 20 Javascript
json对象转为字符串,当做参数传递时加密解密的实现方法
Jun 29 Javascript
实现点击下箭头变上箭头来回切换的两种方法【推荐】
Dec 14 Javascript
JS字符串长度判断,超出进行自动截取的实例(支持中文)
Mar 06 Javascript
jQuery中hover方法搭配css的hover选择器,实现选中元素突出显示方法
May 08 jQuery
解决vue-cli + webpack 新建项目出错的问题
Mar 20 Javascript
浅谈jquery fullpage 插件增加头部和版权的方法
Mar 20 jQuery
新版vue-cli模板下本地开发环境使用node服务器跨域的方法
Apr 03 Javascript
安装vue-cli的简易过程
May 22 Javascript
vue 集成 vis-network 实现网络拓扑图的方法
Aug 07 Javascript
原生JavaScript实现的无缝滚动功能详解
Jan 17 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
Laravel 自动转换长整型雪花 ID 为字符串的实现
2020/10/27 PHP
jQuery 获取URL参数的插件
2010/03/04 Javascript
解析window.open的使用方法总结
2013/06/19 Javascript
jquery eval解析JSON中的注意点介绍
2013/08/23 Javascript
javaScript基础语法介绍
2015/02/28 Javascript
AngularJS中实现用户访问的身份认证和表单验证功能
2016/04/21 Javascript
Bootstrap表单组件教程详解
2016/04/26 Javascript
jQuery选择器及jquery案例详解(必看)
2016/05/20 Javascript
JS实现京东首页之页面顶部、Logo和搜索框功能
2017/01/12 Javascript
关于iframe跨域POST提交的方法示例
2017/01/15 Javascript
全面解析Node.js 8 重要功能和修复
2017/06/02 Javascript
ReactNative Image组件使用详解
2017/08/07 Javascript
Node.js使用Koa搭建 基础项目
2018/01/08 Javascript
详解vue2.0+vue-video-player实现hls播放全过程
2018/03/02 Javascript
妙用缓存调用链实现JS方法的重载
2018/04/30 Javascript
对Vue table 动态表格td可编辑的方法详解
2018/08/28 Javascript
详解微信小程序与内嵌网页交互实现支付功能
2018/10/22 Javascript
JavaScript刷新页面的几种方法总结
2019/03/28 Javascript
webpack结合express实现自动刷新的方法
2019/05/07 Javascript
微信小程序实现上传word、txt、Excel、PPT等文件功能
2019/05/23 Javascript
jquery实现Ajax请求的几种常见方式总结
2019/05/28 jQuery
Vue实现剪贴板复制功能
2019/12/31 Javascript
Vue中登录验证成功后保存token,并每次请求携带并验证token操作
2020/09/08 Javascript
JavaScript实现图片放大预览效果
2020/11/02 Javascript
[03:42]2014DOTA2国际邀请赛 第三日比赛排位扑朔迷离
2014/07/12 DOTA
python操作redis的方法
2015/07/07 Python
学习python之编写简单乘法口诀表实现代码
2016/02/27 Python
pandas 空的dataframe 插入列名的示例
2018/10/30 Python
Python generator生成器和yield表达式详解
2019/08/08 Python
python GUI库图形界面开发之PyQt5中QMainWindow, QWidget以及QDialog的区别和选择
2020/02/26 Python
Python GUI编程学习笔记之tkinter中messagebox、filedialog控件用法详解
2020/03/30 Python
毕业生班级鉴定评语
2015/01/04 职场文书
公司聚餐通知
2015/04/22 职场文书
2015年重阳节主持词
2015/07/04 职场文书
vue3中的组件间通信
2021/03/31 Vue.js
俄罗斯十大城市人口排名,第三首都仅排第六,第二是北方首都
2022/03/20 杂记