9. async函数

async函数是ES2017引入的标准,该函数可以看作是Generator 函数的语法糖,它对Generator 函数进行了改进

基本用法

大概语法如下:

const asyncFn=async function () {
    let res1=await  fetch("/api/isLogin");
    let res2=await fetch("/api/listPositions");
    console.log(res1.json());
    console.log(res2.json());
}

以上代码用来顺序执行判断用户是否登陆、获取城市列表信息

语法

asycn函数返回一个Promise对象,和普通函数一样,只需要执行函数,就会自动执行

async表示函数里有异步操作

async function asyncTest() {}
console.log(asyncTest()) //Promise

在上面的代码中,async后的函数会被转换为一个Promise对象,可以使用then方法添加回调函数

函数内部返回的值会被then方法回调函数接收到,如果在async函数内部抛出错误,会导致返回的Promise对象变为reject状态,抛出的错误对象会被catch函数接收到

async function asyncTest() {
    throw new Error("error")
}

asyncTest()
    .then(
        () => {
            console.log("执行");
        },
        (err)=>{
            console.log(err); //error
        }
    )

await表示紧跟在其后面的表达式需要等待结果,当函数运行时一旦遇到await是就会先暂停执行下面的操作,在等到异步操作完成时再继续执行接下来的函数体内的语句,await命令后可以是Promise对象或数值、字符串和布尔值这些原始值类型,如果是原始值,那么会被立即转换成一个状态为resolve的Promise对象

async function f() {
  return await 123;
}

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

上面代码中,await命令的参数是数值123,它被转成 Promise 对象,并立即resolve。

例如我们举一个例子,设定一个计时器,在计时器内执行完成之后再执行输出值的操作

function timeout(ms) {
    return new Promise((resolve) => {
    setTimeout(()=>{
        console.log("timeout函数正在执行");
        resolve();
        }, ms)
    });
}

async function asyncPrint(value, ms) {
    await timeout(ms);
    console.log(value);
}

asyncPrint("timeout函数执行完成", 100);
//timeout函数正在执行
//timeout函数执行完成

由于async函数返回的是Promise对象,可以作为await命令的参数,所以上面的例子也可以写成下面的形式:

async function timeout(ms) {
    await new Promise((resolve)=>{
        setTimeout(()=>{
            console.log("timeout函数正在执行");
            resolve();
        },ms)
    })
}
async function asyncPrint(value,ms) {
    await timeout(ms);
    console.log(value);
}

asyncPrint('timeout函数执行完成',100);
//timeout函数正在执行
//timeout函数执行完成

async函数返回的是Promise对象,必须等到内部所有await命令后面的Promise对象执行完才会发生状态改变,除非遇到return语句或抛出错误,也就是说只有async函数内部的异步操作执行完才会执行then方法指定的回调函数

async function get() {
    let response = await fetch("/api/isLogin");
    let html = await response.json();
    return html.status;
}

get().then(console.log); //success

上面代码中,函数get内部有两个操作,请求数据,转化数据格式,只有在这两个操作完成后,执行return语句后才会执行then方法中的操作

await命令后面的Promise对象如果变为reject状态,则reject的参数会被catch方法的回调函数接收到,只要一个awite语句后面的Promise变为reject,那么整个async函数都会中断执行

async function fn() {
    await Promise.reject("error"); //这里不需要加return,加上的效果是一样的
    await Promise.resolve("success"); //不会执行
}

fn()
    .then(
        res => {
            console.log(res);
        },
        res => {
            console.log(res);
        }
    )
//error

上面的代码中第二个await语句不会执行,因为第一个await语句状态变为了reject

有时我们希望即使前一个异步操作失败,也不要中断后面的异步操作。这时可以降低一个await放在try...catch结构中,这样不管这个异步操作是否成共,第二个await都会执行

async function fn() {
    try{
        await Promise.reject("error"); 
    }catch(e){
        console.log(e) ////error
    }
    return await Promise.resolve("success"); 
}

fn()
    .then(
        res => {
            console.log(res);
        },
        res => {
            console.log(res);
        }
    )
//success

或者在第一个Promise后再跟一个catch方法,处理前面可能出现的错误

async function fn() {
    await Promise.reject("error")
      .catch(res=>{
          console.log(res); //error
      })
    return await Promise.resolve("success"); 
}

fn()
    .then(
        res => {
            console.log(res);
        },
        res => {
            console.log(res);
        }
    )
//success
错误处理

再上面的代码中我们已经提到过,如果await后面的异步操作出错,那么等同于async函数返回的Promise对象的状态被北纬reject,导致catch方法被调用,后续的异步函数都不会被执行,防止出错的方法,就是将其放置再try...catch代码块中

如果有多个await命令,我们可以统一将其放在try...catch结构中

async function main() {
  try {
    const val1 = await firstStep();
    const val2 = await secondStep(val1);
    const val3 = await thirdStep(val1, val2);

    console.log('Final: ', val3);
  }
  catch (err) {
    console.error(err);
  }
}

下面的例子,使用try...catch结构,实现多次重复尝试

const NUM_RETERIES=3;
async function fn() {
        let i;
         for (i=0;i<NUM_RETERIES;i++){
            try{
                await fetch("/api/isLogin");
                break;
            }catch(err){
               //忽略错误信息
            }
        }
    }
fn();
//代码在try...catch代码块中,如果操作成功,就会使用break语句退出循环;如果失败会被catch语句捕获,然后进入下一轮循环
注意点
  1. 首先就是我们上面在强调的多个await命令后的Promise对象可能时rejected格式,所以最好吧await命令放在try...catch代码块中

  2. 如果多个await命令不存在相互依赖的关系,如果写成先后顺序执行的关系会比较消耗时间,例如两个Ajax数据请求,如果一昧的先后执行就相当于同步操作,完全可以让他们同时触发

    let [foo,bar]=await Promise.all([isLogin(),cityList()])
    //或者
    let isLogin=isLogin();
    let cityList=cityList();
    let login=await isLogin;
    let lists=await cityList;
    

    例如:

    async function timeout() {
     return await  Promise.all([$.get("/api/isLogin"), $.get("/api/cityList")])
    }
    
    timeout()
     .then(
         (e) => {
             console.log(e);
         }
     )
    
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容

  • 异步编程对JavaScript语言太重要。Javascript语言的执行环境是“单线程”的,如果没有异步编程,根本...
    呼呼哥阅读 7,283评论 5 22
  • 简单介绍下这几个的关系为方便起见 用以下代码为例简单介绍下这几个东西的关系, async 在函数声明前使用asyn...
    _我和你一样阅读 21,181评论 1 24
  • 1.async语法 async函数自带执行器。async函数的执行,与普通函数一模一样 await表示紧跟在后面的...
    秦小婕阅读 1,365评论 0 1
  • 弄懂js异步 讲异步之前,我们必须掌握一个基础知识-event-loop。 我们知道JavaScript的一大特点...
    DCbryant阅读 2,686评论 0 5
  • [注:以下代码都在支持 Promise 的 Node 环境中实现] 1 promise 释义 promise 是抽...
    夏暮阅读 682评论 0 0