专栏首页前端一会《深入浅出Node.js》:Node异步编程解决方案 之 async函数

《深入浅出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函数的使用注意点

  1. 由于await命令后面的Promise对象可能失败即rejected会中断整个函数,所以最好把await命令放在try…catch代码块中
  2. 多个await命令后面异步操作如果不存在继发关系,则最好让它们同时触发,方法是使用Promise.all([])
async function fn(){
    try{
        var [r1, r2] = await Promise.all( [getFoo(), getBar()] );
    }catch( e ){}
}
  1. await关键字只能用在async函数中,在其他函数中会报错。

本文分享自微信公众号 - 前端小二(frontendxiao2),作者:小二君

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-02-11

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • JS中的闭包回顾

    在全局环境下,由于有变量提升这个机制在,所以在执行上下文环境中,数据体现的不尽相同:

    前端_AWhile
  • javascript常用技巧

    map() (映射)方法最后生成一个新数组,不改变原始数组的值。其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。

    前端_AWhile
  • 《深入浅出Node.js》:Node异步编程基础–函数式编程

    Node是首个将异步大规模带到应用层面的平台,它从内存运行机制到API设计,都大量使用异步,它的优势在于高性能,但缺点在于异步编程的流程控制其实是有悖于自然语言...

    前端_AWhile
  • JavaScript ES6的一些新鲜玩艺儿。。

    ES6的那些新东西,现在我了解的也不多。 先从简单的来吧,一些个语法看看,, 以前我们声明个对象得这样: var Obj = { data:function(...

    web前端教室
  • JQuery事件处理

    Jquery事件 1、  绑定事件示例代码: <a href=”#”>绑定事件</a> <div style=”display:none;”> 什么是绑定事件?...

    苦咖啡
  • 新人自学前端,如何快速打好前端开发基础?

    怎么样能快速的打好前端开发的基础,然后让自己的技术尽快的提高一个层次?相信这是每一个前端新人都在思考的问题。而这个问题也不止一个同学问过我,怎么样能快速学好前端...

    web前端教室
  • JavaScript学习总结(五)——jQuery插件开发与发布

    jQuery插件就是以jQuery库为基础衍生出来的库,jQuery插件的好处是封装功能,提高了代码的复用性,加快了开发速度,现在网络上开源的jQuery插件非...

    张果
  • JavaScript学习总结(五)——jQuery插件开发与发布

    jQuery插件就是以jQuery库为基础衍生出来的库,jQuery插件的好处是封装功能,提高了代码的复用性,加快了开发速度,现在网络上开源的jQuery插件非...

    张果
  • R-概率统计与模拟(二)

    前文《R-概率统计与模拟》介绍了一些用 R 进行概率模拟的实验,本文继续上次的工作,并在此过程中回顾一些相关的概率统计知识。

    一只羊
  • 选择维度筛选AllSelect和All函数的差异

    此时的度量计算的结果就是数学的平均成绩。如果我们想知道数学和英语的这两门课的综合平均分呢?则在切片器中选择数学和英语即可。

    逍遥之

扫码关注云+社区

领取腾讯云代金券