异步编程 | async/await

ES8提出的async/await关键词旨在解决利用异步结构组织代码的问题

异步函数基础

async

async关键字用于声明异步函数

  1. async关键字可以在函数声明、函数表达式、箭头函数、函数方法上使用
1
2
3
4
5
6
7
8
9
10
11
12
13
// 函数声明
async function foo() {}

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

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

// 函数方法
let o = {
async foo() {}
}
  1. 使用async关键字可以让函数具有异步特征,但总体上函数仍然是同步求值的,在参数或者闭包方面,异步函数仍然具有普通JavaScript函数的正常行为

  2. 异步函数始终返回Promise对象

    • 如果异步函数内部有显式的return语句并且返回值(没有return 值就是undefined),这个值会被Promise.resolve()包装成一个Promsie对象
    • 如果异步函数直接return一个Promise对象,异步函数返回值就是那个Promise对象
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    async function foo() {
    console.log(1);
    return 3;
    }

    // 给返回的promise添加一个then方法的解决处理程序
    foo().then(console.log);

    console.log(2);

    // 1 这个是foo()调用得到的
    // 2
    // 3 这个是then()处理的结果


    async function foo() {
    console.log(1);
    return Promise.resolve(3);
    }

    foo().then(console.log);

    console.log(2);

    // 1 这个是foo()调用得到的
    // 2
    // 3 这个是then()处理的结果
  3. 异步函数的返回值期待一个实现thenable接口的对象,但常规值也可以

    • 如果返回的是实现thenable接口的对象,则这个对象可以由提供给then()的处理程序“解包”
    • 如果不是,则返回子和就被当作已解决的Promise
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    // 返回一个原始值
    async function foo() {
    return 'foo';
    }
    foo().then(value => console.log(value)); // 'foo'

    // 返回一个没有实现thenable接口的对象
    async function bar() {
    return ['bar']
    }
    bar().then(value => console.log(value)); // ['bar']

    // 返回一个实现thenabke接口的对象
    async function baz() {
    const thenable = {
    then(callback) {
    callback('baz');
    }
    };

    return thenable;
    }
    baz().then(value => console.log(value)); // 'baz'
  4. 在异步函数中抛出错误会返回拒绝的Promise

1
2
3
4
5
6
7
8
9
async function foo() {
console.log(1);
throw 3;
} // foo为 Promise{<rejected>:3}

foo().catch(reason => console.log(reason));

// 1 foo()的结果
// 3 catch()的结果
  1. 拒绝Promise的错误不会被异步函数捕获
1
2
3
4
5
6
7
8
9
10
async function foo() {
console.log(1);
Promise.reject(3);
}

foo().catch(console.log);
console.log(2);
// 1
// 2
// Uncaught (in promise): 3

await

await关键词可以暂停异步函数代码的执行,等待Promise的解决

  1. await可以单独使用也可以在表达式中使用

  2. await 关键字会暂停执行异步函数后面的代码,让出JavaScript 运行时的执行线程,这个行为与生成器函数中的yield 关键字是一样的,await 关键字同样是尝试“解包”对象的值,然后将这个值传给表达式,再异步恢复异步函数的执行

  3. await关键字期待(但实际上并不要求)一个实现thenable 接口的对象,但常规的值也可以,如果是实现thenable 接口的对象,则这个对象可以由await 来“解包”,如果不是,则这个值就被当作已经解决的Promise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 等待一个原始值
async function foo() {
console.log(await 'foo');
}
foo();
// foo

// 等待一个没有实现thenable 接口的对象
async function bar() {
console.log(await ['bar']);
}
bar();
// ['bar']

// 等待一个实现了thenable 接口的非期约对象
async function baz() {
const thenable = {
then(callback) { callback('baz'); }
};
console.log(await thenable);
}
baz();
// baz

// 等待一个期约
async function qux() {
console.log(await Promise.resolve('qux'));
}
qux();
// qux
  1. await会抛出错误的同步操作,会返回拒绝的Promise
1
2
3
4
5
6
7
8
9
10
11
12
13
async function foo() {
console.log(1);
await (() => {throw 3})();
}

foo().catch(reason => {
console.log(reason);
})
console.log(2);

// 1
// 2
// 3
  1. 对拒绝的Promise使用await则会释放(unwrap)错误值(将拒绝Promise返回)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
async function foo() {
console.log(1);
await Promise.reject(3);
console.log(4); // 这行代码不会执行
}

foo().catch(reason => {
console.log(reason);
})
console.log(2);

// 1
// 2
// 3
  1. await关键字有一定限制

    • await关键字必须在异步函数中使用,不能在顶级上下文如