《深入浅出Node.js》:Node异步编程解决方案 之 async函数
关于async函数,需要明确它是generator函数的语法糖,即将生成器函数的*
换成async
关键字,将yield
关键字换成await
关键字。使用async函数相比于生成器函数的改进主要在于前者具备内置执行器,即直接调用async函数就能执行完整个函数,就像普通函数调用那样,而无需像生成器函数通过调用返回的迭代器的next()方法来手动执行后续代码,非常方便。此外语义化更友好,并且async函数返回的还是一个Promise对象,可以使用then()方法来指定下一步操作。
async函数基本用法
当async函数执行时,一旦遇到await关键字就会先返回,等到异步操作完成,然后再接着执行函数体后面的代码。
function timeout( ms ) {
return new Promise( function ( resolve, reject ) {
setTimeout( resolve, ms )
} )
}
async function asyncPrint( value, ms ){
console.log( "开始" );
await timeout( ms );
console.log( value );
}
asyncPrint( "Hello Nitx.", 1000 );
//打印:
/*
开始
// 1s后打印
Hello Nitx.
*/
async函数返回Promise对象
async函数返回一个Promise对象,async函数内部return
语句返回的值,会变成then方法回调函数的参数。
async function fn(){
return "Hello nitx";
}
fn()
.then( function ( data ) {
console.log( data );
} )
.catch( function ( err ) {
console.log( err );
} )
// Hello nitx
async函数内部抛出错误,会导致返回的Promise对象转变为reject状态。所以抛出的错误就会被后面的catch()方法回调函数捕获:
async function fn() {
throw new Error( "errmsg" );
}
fn()
.then( function ( data ) {console.log( data );} )
.catch( function ( err ) {
console.log( err ); // Error: errmsg
} )
async函数返回的Promise对象必须等到内部所有await命令后的异步操作执行完才会执行then方法指定的回调函数,除非遇到return语句或抛出错误。
var ajaxJSON = function( method ){
var method = (method || "GET").toUpperCase();
return function ( url ) {
return new Promise( function ( resolve, reject ) {
var xhr = new XMLHttpRequest();
xhr.open( method, url );
xhr.responseType = "json";
xhr.setRequestHeader( "Accept", "application/json" );
xhr.onreadystatechange = handler;
xhr.send();
function handler() {
if( this.readyState !== 4 ){
return;
}
if( this.status === 200 ){
resolve( this.response );
}else {
reject( new Error( this.statusText ) )
}
}
} )
}
}
var getJSON = ajaxJSON();
async function fn( url ){
var res = await getJSON( url );
return res; // async函数中return语句返回的数据会作为then()方法的回调函数的参数
}
fn( "https://api.github.com/users/Bournen" )
.then( function ( data ) {
console.log( data.url );
} )
.catch( function ( err ) {
console.log( err );
} )
async函数中异步操作的错误处理
在async函数中异步操作出错时,等同于Promise对象的reject过程,也就是会被后面的catch()方法的回调函数捕获为参数:
async function fn( url ){
var res = await getJSON( url );
return res;
}
fn( "https://api.github.com/users/Bournen22" ) // 故意设置此处url路径错误
.then( function ( data ) {
console.log( data.url );
} )
.catch( function ( err ) {
console.log( err ); // Error: Not Found
} )
这里就有个问题需要指出,用以上写法时,如果async函数中某个异步操作出错时会导致整个async函数中断并抛出错误,如果后面还有其他异步操作也是不会执行到的:
async function foo() {
await Promise.reject( "错误了,这里会导致整个async函数中断" );
await Promise.resolve( "这里的异步操作本想执行的,但现在被前面错误导致整个async函数中断了" );
}
foo()
.then( function ( data ) {
console.log( data );
} )
.catch( function ( err ) {
console.log( err );
} )
// 错误了,这里会导致整个async函数中断
但有时如果希望即使前面的异步操作失败,也不会影响中断的异步操作执行。可以将await放在try...catch...
结构中。这样不管前面个异步是否会成功,后面的异步都会执行:
async function foo() {
try{
await Promise.reject( "错误了,这里会导致整个async函数中断" );
}catch( e ){
}
return await Promise.resolve( "现在即使前面的异步操作失败,我也可以执行了" );
}
foo()
.then( function ( data ) {
console.log( data );
} )
.catch( function ( err ) {
console.log( err );
} )
// 现在即使前面的异步操作失败,我也可以执行了
所以通常来说,在async函数中,防止出错导致中断整个函数执行的较佳实践是使用try...catch
代码块。
如果有多个await命令,可以统一放在try...catch
代码块中:
async function fn(){
try{
await first();
await second();
await third();
}catch( e ){}
return "HelloWorld";
}
也可以使用try...catch
代码块实现多次重复尝试,例如多次重复访问:
var getJSON = ajaxJSON();
var count = 3;
async function fn( url ){
for( var i=0; i<count; i++ ){
try{
var res = await getJSON( url );
break;
}catch( err ){
/* 忽略错误,继续执行 */
}
}
return res;
}
fn( "https://api.github.com/users/Bournen22" )
.then( function ( data ) {
console.log( data.url );
} )
.catch( function ( err ) {
console.log( err );
} )
在上例中,如果await异步操作成功就会使用break退出循环,如果失败会被catch语句捕获并进入下一个循环。
async函数的使用注意点
- 由于await命令后面的Promise对象可能失败即rejected会中断整个函数,所以最好把await命令放在
try...catch
代码块中 - 多个await命令后面异步操作如果不存在继发关系,则最好让它们同时触发,方法是使用
Promise.all([])
async function fn(){
try{
var [r1, r2] = await Promise.all( [getFoo(), getBar()] );
}catch( e ){}
}
- await关键字只能用在async函数中,在其他函数中会报错。
喜欢本文请扫下方二维码,关注微信公众号: 前端小二,查看更多我写的文章哦,多谢支持。
- 机器学习的技术栈及应用实例脑洞
- Ryu:OpenFlow协议源码分析
- 详解 LSTM
- 并发系列:从原子更新到CPU锁
- 前后端分离之vue2.0+webpack2 实战项目 -- webpack介绍
- 神经网络 之 感知器的概念和实现
- 图解ByteBuffer
- TensorFlow-1: 如何识别数字
- 你应该知道的Java垃圾收集器 - 串行、并行、CMS、G1
- ckplayer 如何在PC上完美支持 m3u8播放
- TensorFlow-2: 用 CNN 识别数字
- js图片前端预览之 filereader 和 window.URL.createObjectURL
- TensorFlow-3: 用 feed-forward neural network 识别数字
- TensorFlow-4: tf.contrib.learn 快速入门
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 谷歌开源NLP模型可视化工具LIT,模型训练不再「黑箱」
- Python 装饰器填坑指南 | 最常见的报错信息、原因和解决方案
- 社区开源框架预制件相关模块:CollectManager详解
- Kettle构建Hadoop ETL实践(三):Kettle对Hadoop的支持
- 3种 Springboot 全局时间格式化方式,别再写重复代码了
- 一文搞懂CDN加速原理
- 3分钟短文:Laravel Carbon自定义日期时间格式
- (30)字符截取命令sed
- 2. Bean Validation声明式校验方法的参数、返回值
- 如何识别和阻止基于电报的僵尸网络
- 由String,String Builder,String Buffer 引起的面试惨案
- MySQL案例:各类临时文件的存放位置
- 关于加@Transactional注解的方法之间调用,事务是否生效的问题
- 基于Vue实现一个有点意思的拼拼乐小游戏
- 硬核手写字节码实现HelloWorld