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

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的语法

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值