ES6

9. async函数

2018-01-11  本文已影响0人  羊烊羴

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);
         }
     )
    
上一篇下一篇

猜你喜欢

热点阅读