ES6 异步编程解决方案 之 Async

一、async 函数的基本用法

  • async 函数返回一个 Promise 对象,可以使用 thencatch 方法 添加回调函数

  • async 函数执行时,一旦遇到 await 就会先返回,等到异步操作完成,再接着执行函数体内后面的语句 【异步函数 同步执行】

  • async 函数有很多种形式:

// 函数声明
async function foo() {}

// 函数表达式
const foo = async function () {};

// 箭头函数
const foo = async () => {};

// Class 的方法
class Storage {
  constructor() {
    this.cachePromise = caches.open('avatars');
  }

  async getAvatar(name) {
    const cache = await this.cachePromise;
    return cache.match(`/avatars/${name}.jpg`);
  }
}

// 对象的方法
let obj = { async foo() {} };
obj.foo().then(...)
  • 示例: 指定多少毫秒后输出一个值
function timeout(ms) {
    return new Promise((resolve) => {
        setTimeout(resolve, ms);
    });
}

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

asyncPrint('hello world', 50);

// 上面代码指定 50 毫秒以后,输出 hello world

二、async 语法

1. async 函数

  • async 函数的返回值 是 Promise 对象 (可调用 thencatch 方法)

  • async 函数内部抛出错误,会导致返回的 Promise 对象变为 reject 状态

  • async 函数的回调方法 thencatch 的参数

    • async 函数内部 return 关键字返回的值,会 作为 thencatch 方法的参数

    • async 函数的返回的 Promise 对象 状态为 resolve 时,函数内部 若没有返回值,thencatch 方法的参数 为 undefined;调用 then 方法

    • async 函数的返回的 Promise 对象 状态为 reject 时,函数内部 若没有返回值,thencatch 方法的参数 为 错误对象;调用 catch 方法

async function f() {
    return await Promise.resolve(123);
}

f().then(v => console.log(v));  // 123
async function f() {
    await Promise.resolve(123);
}

f().then(v => console.log(v));  // undefined
async function f() {
  throw new Error('出错了');
}

f().then(
  v => console.log(v),
  e => console.log(e)   
)   // Error: 出错了

2. await 关键字

  • await 关键字 只能在 async 函数中使用: await 后面 如果跟异步函数,程序执行时会等 await 后面的 异步函数执行完之后,再执行 后面的代码 【异步函数 同步执行】

  • 正常情况下,await 命令后面是一个 Promise 对象;如果不是,会转成 Promise 对象,并立即 resolve

// 只要有一个 `await` 后面的函数执行错误,程序就会停止执行代码

async function f() {
    await Promise.reject('出错了');
    await Promise.resolve('hello world'); // 不会执行
}
    
f().then(
    (result) => {
        console.log(result);
    },
    (e) => {
        console.log(e);     
    }
);      // 出错了

三、async 应用

1. 一个 async 函数中,有多个 await 关键字:使用 try..catch( ) 避免程序终止

  • 场景: 只要有一个 await 后面的函数执行错误,程序就会停止执行代码; async 函数返回的 Promise 对象的状态为 reject

  • 解决方案:

    • 方案 1:使用 try...catch() 把有可能出错的 操作放到 try 语句中

    • 方案 2:await 后面的 Promise 对象再跟一个 catch 方法,处理前面可能出现的错误

// 解决方案 1:try...catch() 即使有一个异步操作失败,也不终止程序

async function f() {
    try {
        await Promise.reject('出错了');
    } catch(e) {
        console.log(e);
    }
    
    return await Promise.resolve('hello world');
}

f().then(v => console.log(v))
// hello world
// 解决方案 2:catch() 即使有一个异步操作失败,也不终止程序

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

2. 一个 async 函数中,有多个 await 关键字:使 await 后面的异步操作 同时执行

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

// 写法二
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;

3. 使用 try...catch() 结合 asyncawait 实现 ajax 请求轮询

const NUM_RETRIES = 3;

async function ajaxRequest() {
    for (let i = 0; i < NUM_RETRIES; ++i) {
        try {
            await $.get('http://google.com/this-throws-an-error');
            break;
        } catch(err) {
            console.log(err);
        }
    }
}

ajaxRequest();

四、async for await...of ---> 异步遍历

  • 遍历时,有一个异步请求报错,都会导致程序终止;所以,结合 try...catch() 解决此类问题
async function () {
    try {
        for await (const x of createRejectingIterable()) {
            console.log(x);
        }
    }
    catch (e) {
        console.error(e);
    }
}

五、async 与 其他异步解决方案的 比较

  • Promise: 比回调函数的写法大大改进,但是一眼看上去,代码完全都是 Promise 的 API(then、catch等等),操作本身的语义反而不容易看出来

  • Generator: 语义比 Promise 写法更清晰,需要有 任务运行器,自动执行 Generator 函数;但偏底层的写法

  • async: Generator 函数的语法糖,封装了 Generator 的底层机制 ,实现最简洁,最符合语义;

posted @ 2018-03-12 11:40  执着的程序员~  阅读(198)  评论(0编辑  收藏  举报