博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ES6&ES7中的异步之async函数
阅读量:7165 次
发布时间:2019-06-29

本文共 5534 字,大约阅读时间需要 18 分钟。

async函数

定义

async函数其实就是之前说过的Generator的语法糖,用于实现异步操作。它是ES2017的新标准。

读取两个文件:

const fs = require('fs')const readFile = function(filename){    return new Promise(function(resolve,reject){        fs.readFile(filename,function(error,data)){            if(error) return reject(error)            resolve(data)        }    })}const gen = function* (){    const gen1 = yield readFile('../1.txt')    const gen2 = yield readFile('../2.txt')    console.log(gen1.toString())    console.log(gen2.toString())}

如果使用async函数的话,会是这么写

const _readFile = async function(){    const r1 = await readFile('../3.txt')    const r2 = await readFile('../4.txt')    console.log(r1.toString())    console.log(r2.toString())}

一般情况下,只是把Generator的*换成async,把yield换成await。

async函数是Generator函数的改进,具体体现在四点

1.内置执行器:

相对于Generator函数需要co模块或者next()方法来作为执行器执行,async函数自带执行器,所以上边的代码只需要一句

_readFile()

可以执行。

2.更好的语义:

async和await分别表示异步和等待,比起*和yield更容易理解。

3.更广泛的适用性:

yield后边只能跟Thunk函数或者Promise对象,但是在async函数中,可以跟Promise对象和基本数据类型。

4.返回值是Promise

相比于Generator函数返回一个Iterator还需要遍历,async直接返回一个Promise可以直接调用then方法和catch方法。

基本用法

async函数返回一个Promise对象,可以使用then方法添加回调,然后使用await关键字后,会等到异步操作执行完在执行后边的语句。

async function getPriceByName(name){    const symbol = await getSymbod(name)    const price = await getPrice(symbol)    return price}getPriceByName('WUBA').then(function(result){    console.log(result)})

上边的例子,是一个通过股票的名称,获得股票的代码,再获得价钱。前边声明async关键字,表示内部有内部操作,调用函数会返回一个Promise对象。

再看下一个例子,是一个指定多少毫秒后返回一个值。

function TimeOut(time){    return new Promise(function(resolve)){        setTimeout(resolve,time)    }}async asyncTimeOut = function(value,time){    const t1 = await TimeOut(time)    console.log(t1)}asyncTimeOut('hello',1000)asyncTimeOut('world',2000)

async函数的多种形式

函数表达式const fun1 = async function(){    ....}函数声明async function fun2(){    ....}箭头函数const fun3 = async () => {    ....}对象中的变量let obj = {    async fun4(){        ....    }}obj.fun4()类的写法class Async {    constructor(){}        async fun5(){        ....    }}const a1 = new Async()a1.fun5().then()

语法

返回一个Promise对象

说过很多次了,async函数返回一个Promise对象。

函数return的值将会作为then方法的参数

async function show(){    return '123'}show().then((v) => console.log(v))

show方法返回的值会被作为then方法的参数而调用。

如果在async函数内部抛出错误,会被catch捕获,这个时候Promise对象变成reject状态。

async function show2(){    throw new Error('出错了')}show2().then(    v => console.log(v),    e => console.log(e))

Promise的状态改变

async函数返回的Promise对象,必须等到函数内部所有的await执行完才会发生状态的变化,也就是说,得等到所有await执行完,才会执行后续的then方法。

async function getText(url){    const response = await fetch(url)    const text = await response.text()    return text.match('../aa/[a-z]}')[1]   //反正就是一个正则匹配}const url = '....'getText(url).then(v => console.log(v))

这个例子说明,得等到两个awiat都执行完才会console返回的数据。

await命令

前边说过,await命令后边跟随一个Promise对象。如果不是,会被转成Promise对象。

async function show(){    return await '123'}show().then((v) => console.log(v))

如果await后边的Promise对象变成了reject状态,会被后边的catch()捕获。

async function fun1() {    return await Promise.reject('出错了')}fun1().catch(e => console.log(e))

如果async函数内部有多个await,但是只要一个await返回的Promise对象变成了reject状态,则整个函数立刻捕获异常。

如果想要前边的正常抛出异常而不影响后边的await语句执行,可以把前边的写进一个try/catch中去。

async function fun2(){    try{        await Promise.reject('出错了')    }catch(e){    }        await Promise.resolev('hello')}fun2().then(v => console.log(v))

使用的注意点

  • 由于await后面跟随的是Promise对象,所以对象可能会有两个状态,一个resolve一个reject。所以,最好把await代码放到try/catch语句中比较好。

async function fun3(){    try{        await asyncFun1()        await asyncFun2()    } catch(e){        console.log(e)    }}// 还有另外一种写法async function fun4(){    await asyncFun1().catch(e => console.log(e))    await asyncFun2().catch(e => console.log(e))}

还是第一种方法更好一点。直接写进try/catch语句中。

  • 最好让多个await后边的异步操作同时发生,如果不是不存在先后顺序的话。

let a1 = await get1()let a2 = await get2()

上边的写法,get1执行完之后才会执行get2,如果get1和get2没有直接的关联,那样会很浪费时间。

//同时触发let [a1,a2] = await Promise.all([get1(),get2()])
  • 如果await放到async函数之外,就会报错,只能放到async函数内部。

async的原理

原理也很简单,就是把Generator函数和自动执行器包装在一个函数中。

async function fn(){    .....}// 等价于function fn(){    return spawn(function *(){        .....    })}

其中spawn函数就是自动执行器。

function spawn(genF) {  return new Promise(function(resolve, reject) {    const gen = genF();    function step(nextF) {      let next;       try {        next = nextF();      } catch(e) {        return reject(e);      }      if(next.done) {        return resolve(next.value);      }      Promise.resolve(next.value).then(function(v) {        step(function() { return gen.next(v); });      }, function(e) {        step(function() { return gen.throw(e); });      });    }    step(function() { return gen.next(undefined); });  });}

实例:按照顺序完成异步操作

实际开发中经常会遇到各种异步操作,这里有一个例子。一次读取一组url,然后按照读取的顺序返回结果。

function readUrls(urls) {    const textPromise = urls.map(url => {        fetch(url).then(response => response.text())    })        // 按照顺序读出    textPromise.reduce((chain,textPromise) => {        return chain.then(() => textPromise)        .then(text => console.log(text));    },Promise.resolve())}

分析一下,上边的代码,用fetch同时读取一种url,每个fetch操作都返回一个Promise对象,放入textPromise数组,然后reduce方法一次处理每个Promise对象,然后用then连接起来,一次输出结果。

缺点:这种方法看起来不太好理解,不太直观,用async函数会更好一点。

async function readUrls(urls){    for(const url of urls){        const response = await fetch(url)        console.log(response.text())    }}

可以看到,代码是大大简化了,但是会有一个新的问题,就是必须等到前边一个读完了,才会读取下一个数据。

function readUrls(urls){    const textPromise = urls.map(async url => {        const response = await fetch(url)        return response.text()    })}//按照顺序输出for(const text of textPromise){    console.log(await text)}

上边的函数,虽然map方法的参数是async函数,但却是并发执行的,因为内部是继发执行,不影响外部。在后边的循环中使用了await,这样,还是会依次输出。

转载地址:http://ppqwm.baihongyu.com/

你可能感兴趣的文章
教你如何实现 Android TextView 文字轮播效果
查看>>
UITableView
查看>>
ContextMenu和OptionsMenu主要有以下区别
查看>>
iptables详解
查看>>
Mysql 分区介绍(九) —— 分区管理
查看>>
exchange2010---将邮箱置于保留挂起中---Retention Hold
查看>>
Vert.x系列(四)-- HAManager源码分析
查看>>
Java 8 中的 Streams API 详解
查看>>
搭建安全的Web服务器
查看>>
初学者
查看>>
小叙安装CentOS 6.5
查看>>
LVM 扩展/减缩/镜像 【有图有真相】
查看>>
Live Migrate 操作 - 每天5分钟玩转 OpenStack(42)
查看>>
我的友情链接
查看>>
免费为网站加上HTTPS
查看>>
银河麒麟操作系统上配置Bond(二)使用ifenslave方式
查看>>
Python高阶函数
查看>>
Xen虚拟化
查看>>
spring 数据连接池
查看>>
免软驱 安装 windows server 2003 RAID驱动 -GenerateDriverDiskISO
查看>>