ES6学习笔记(十六)async函数

本文深入讲解async函数,探讨其作为Generator函数语法糖的特性,包括内置执行器、语义改进及返回Promise对象的优势。通过实例演示async函数的使用,解析await命令及异常处理技巧。

async函数简介


async函数简单来说就是Generator函数的语法糖,可以看成多个异步操作包装成的Promise对象。相比较于Generator函数,async函数多了几个改进的地方

1.内置执行器

在使用Generator函数时,我们需要自己设置一个执行器,但是async自带执行器,在使用时与普通函数一样只需要函数名和括号来传入参数。

2.更好的语义

相对于Generator函数的声明,async的声明其实很像,但更符合语义,看看下面两个函数

//Generator函数

function* gen(){

    yield 1;

}

//async函数

async function gen(){

    await 1;

}

async函数只是将*改成async,把yield改成await。

3.返回值是Promise对象

Generator函数返回的是Iterator对象,而async函数返回的是Promise对象,使用then方法来指定下一步操作,比Iterator对象使用next方便。

async函数的使用


async函数在使用时,会直接执行函数体的内容,遇到await语句时,会先返回Promise对象,执行完该await语句后再继续执行后面的内容。

async function asy(){
    console.timeline();
    await new Promise(resolve=>setTimeout(()=>{
        resolve();
        console.log(1);
    },3000));
    setTimeout(()=>{
        console.log(2);
        console.timelineEnd();
    },1000);
}

asy()

// 1

// 2

// Timeline 'default': 4003.81005859375ms

上面代码中,1是3秒后才打印,2是1秒后打印,在一般函数中,2会先打印,但因为在async函数中,需要等到await语句后面的内容执行完后才会执行下面的内容,所以这里先要等到await后面的Promise对象转换为resolve状态后才能继续执行下面的内容,所以这里2其实是4秒后才打印。从使用console.timelineEnd打印出的时间也能看出。

async函数的语法


返回Promise对象

async函数返回的是一个Promise对象,即最后return的内容会变为then方法的resolve方法的参数,而抛出的错误会被catch方法捕获。

async function asy(){

    return 'i am promise';

}

asy().then(val=>console.log(val))

// i am promise



async function asy(){

    throw new Error('i throw error');

}

asy().then().catch(e=>console.log(e))

// Error: i throw error

async函数返回的Promise对象状态的改变需要等到async函数中所有await语句后面的Promise对象转换完才会改变,除非遇到return语句或者抛出错误。

async function asy(){

    await new Promise((resolve)=>setTimeout(resolve,2000));

}

console.timeline();

asy().then(()=>{

console.log('complete');

console.timelineEnd();

})

//complete

// Timeline 'default': 2001.083251953125ms

从打印出来的时间可以看出,complete是在两秒后才打印出来的,即是等到里面的Promise对象转换为resolve状态才转换的。

await命令

正常情况下,await命令后面是一个Promise对象,若不是Promise对象,则直接返回该值。

async function asy(){

    return await 'not promise';

}

asy().then(val=>console.log(val))

// not promise

如上面代码,后面的字符串不是Promise对象,所以直接将该值return出来,作为resolve方法的参数。

当然,thenable对象一样会被当成Promise对象处理(带then方法的对象)。

var obj={

    then(resolve){

        setTimeout(()=>{

            resolve();

            console.log('thenable');

        },1000)

    }

}

async function asy(){

    await obj;

    console.log('async');

}

asy().then(()=>console.log('promise'))

Promise {<pending>}

// thenable

// async

// promise

上面代码可以看到,虽然obj对象里面的then方法的console语句是在1秒后执行的,但’thenale’字符串还是在’promise’字符串之前打印出来,说明await将obj对象当成Promise对象处理。

要注意的是,如果async函数中有一个Promise对象转换为rejected状态,整个函数会终止执行。

async function asy(){

    await Promise.reject('error');

    await Promise.resolve('continue');

console.log('complete');

}

asy().then(val=>console.log(val)).catch(e=>console.log(e))

// error

上面代码中,因为第一个Promise对象变为rejected状态,所以后面的内容都没有执行了。为了避免这种情况,一般将可能会出错或者变为rejected状态的Promise对象放在try...catch语句块中,或者在其后面调用catch方法。

async function f() {

  try {

    await Promise.reject('出错了');

  } catch(e) {

  }

  return await Promise.resolve('hello world');}

f().then(v => console.log(v))

// hello world



async function f() {

  await Promise.reject('出错了')

    .catch(e => console.log(e));

  return await Promise.resolve('hello world');}

f().then(v => console.log(v))

// 出错了

// hello world

如上面代码,在转为rejected状态的Promise对象后面的语句依然能执行。

使用注意点


1.使用async函数时,因为其中的await语句后面的Promise对象可能会变为rejected状态,所以最好将其放在try...catch语句中。

2.多个异步操作执行时,若其执行的顺序与最终结果无关,最好让它们同时触发。

// 按顺序执行的写法

let foo = await getFoo();

let bar = await getBar();

// 同时触发

// 写法一

let [foo, bar] = await Promise.all([getFoo(), getBar()]);

// 写法二

let fooPromise = getFoo();

let barPromise = getBar();

let foo = await fooPromise;

let bar = await barPromise;

3.await命令只能在async函数中使用,若在其他地方使用会报错。

function normal(){

await 1;

}

// Uncaught SyntaxError: await is only valid in async function

4.async函数可以保留运行堆栈

const a = () => {

  b().then(() => c());

};

上面代码中,a内部执行了一个异步方法b,但是在b开始执行时,a并不会停止执行,等到c执行时,a可能已经停止了。b所在的上下文环境已经不在了,若b()或c()报错,错误堆栈将不包括a()。

执行原理


async函数的执行原理,其实就是将Generator函数和自动执行器包装在一个函数里。

async function fn(args) {

  // ...

}

// 等同于

function fn(args) {

  return spawn(function* () {

// ...  

});}

参考自阮一峰的《ECMAScript6入门》


ES6学习笔记目录

 

ES6学习笔记(一)let和const

ES6学习笔记(二)参数的默认值和rest参数

ES6学习笔记(三)箭头函数简化数组函数的使用

ES6学习笔记(四)解构赋值

ES6学习笔记(五)Set结构和Map结构

ES6学习笔记(六)Iterator接口

ES6学习笔记(七)数组的扩展

ES6学习笔记(八)字符串的扩展

ES6学习笔记(九)数值的扩展

ES6学习笔记(十)对象的扩展

ES6学习笔记(十一)Symbol

ES6学习笔记(十二)Proxy代理

ES6学习笔记(十三)Reflect对象

ES6学习笔记(十四)Promise对象

ES6学习笔记(十五)Generator函数

ES6学习笔记(十六)asnyc函数

ES6学习笔记(十七)类class

ES6学习笔记(十八)尾调用优化

ES6学习笔记(十九)正则的扩展

ES6学习笔记(二十)ES Module的语法

 

 

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值