await 到底在等什么?

41 阅读2分钟

先说结论:await 在等待一个“确切”的结果

总的来说,await 等待有两种情况:函数其它值

一、等待函数返回值

1. 返回普通值

<script>
    function func() {
        // 返回 非 thenable 且 非 Promise - await 不额外等待 then
        return 'aaa'
    }

    async function test() {
        console.log(1);
        await func()
        console.log(3);
    }

    test();

    new Promise(resolve => {
        console.log(4)
        resolve()
    }).then(() => {
        console.log(6)
    }).then(() => {
        console.log(7)
    })

    console.log(5);
    
    // 最终输出:1 4 5 3 6 7
</script>

2. 返回 thenable 类型值 - 需额外等待一个 then

<script>
    function func() {
        // 返回 thenable - await 额外等待一个 then
        return {
            then(cb) {
                cb()
            }
        }
    }

    async function test() {
        console.log(1);
        await func()
        console.log(3);
    }

    test();

    new Promise(resolve => {
        console.log(4)
        resolve()
    }).then(() => {
        console.log(6)
    }).then(() => {
        console.log(7)
    })

    console.log(5);
    
    // 最终输出:1 4 5 6 3 7
</script>

3. 返回 Promise 对象

3.1 有结果且没有 then - 需额外等待两个 then,后来做了优化,无需额外等待
<script>
    function func() {
        // 返回有结果的 Promise 对象 - await 原来是要额外等待两个 then。后来做了优化,无需额外等待
        return new Promise((resolve) => {
            console.log('B')
            resolve()
        })
    }

    async function test() {
        console.log(1);
        await func()
        console.log(3);
    }

    test();

    new Promise(resolve => {
        console.log(4)
        resolve()
    }).then(() => {
        console.log(6)
    }).then(() => {
        console.log(7)
    }).then(() => {
        console.log(8)
    }).then(() => {
        console.log(9)
    })

    console.log(5);
    
    // 最终输出:1 B 4 5 6 7 3 8 9
</script>
3.2 有结果且有 then - 待返回的 Promise 的 then 执行完后,还需额外等待两个 then。后来做了优化,无需额外等待
<script>
    function func() {
        // 返回有结果的 Promise 对象 - await 原来是要额外等待两个 then,后来做了优化,无需额外等待
        return new Promise((resolve) => {
            console.log('B')
            resolve()
        }).then(() => {
            console.log("C")
        }).then(() => {
            console.log("D")
        })
    }

    async function test() {
        console.log(1);
        await func()
        console.log(3);
    }

    test();

    new Promise(resolve => {
        console.log(4)
        resolve()
    }).then(() => {
        console.log(6)
    }).then(() => {
        console.log(7)
    }).then(() => {
        console.log(8)
    }).then(() => {
        console.log(9)
    })

    console.log(5);
    
    // 最终输出:1 B 4 5 C 6 D 7 8 3 9
</script>
3.3 无结果 - await 后面的代码永远不会执行
<script>
    function func() {
        // 返回无结果的 Promise 对象
        return new Promise(resolve => {
            console.log("B")
            // resolve()   // 故意不产生结果,让这个 Promise 一直为 pending 状态,那么 log(3) 永远不会执行
        })
    }

    async function test() {
        console.log(1);
        await func()
        console.log(3);
    }

    test();

    new Promise(resolve => {
        console.log(4)
        resolve()
    }).then(() => {
        console.log(6)
    }).then(() => {
        console.log(7)
    }).then(() => {
        console.log(8)
    }).then(() => {
        console.log(9)
    })

    console.log(5);
    
    // 最终输出:1 B 4 5 C 6 D 7 8 3 9
</script>

二、等待其它值

1. 等待普通值 - 同函数返回普通值是一样的效果

2. 等待 thenable 类型值 - 同函数返回此类型值是一样的效果

3. 等待 Promise 对象

3.1 有结果且无 then - 这个不是等待 async 返回的 Promise,所以无需额外等待
<script>
    async function test() {
        console.log(1);
        // 有结果,但这个不是等待 async 返回的 Promise,所以无需额外等待
        await new Promise(resolve => {resolve()}) 
        // await Promise.resolve() 是一个意思
        console.log(3);
    }

    test();

    new Promise(resolve => {
        console.log(4)
        resolve()
    }).then(() => {
        console.log(6)
    }).then(() => {
        console.log(7)
    }).then(() => {
        console.log(8)
    }).then(() => {
        console.log(9)
    })

    console.log(5);
    
    // 最终输出:1 4 5 3 6 7 8 9 
</script>
3.2 有结果且有 then - 这个不是等待 async 返回的 Promise,所以无需额外等待
<script>
    async function test() {
        console.log(1);
        // 有结果
        await new Promise(resolve => {
           resolve()
        }).then(() => {
           console.log(10)
        })
        
        console.log(3);
    }

    test();

    new Promise(resolve => {
        console.log(4)
        resolve()
    }).then(() => {
        console.log(6)
    }).then(() => {
        console.log(7)
    }).then(() => {
        console.log(8)
    }).then(() => {
        console.log(9)
    })

    console.log(5);
    
    // 最终输出:1 4 5 10 6 3 7 8 9
</script>
3.3 无结果 - 和等待 async 返回无结果的 Promise 一样,await 后面的代码永远不会执行

那么问题来了,await 相当于什么?

来看以下代码,输出什么?

<script>
    async function fun() {
        console.log(2)

        await "a"
        console.log("A")
        await "b"
        console.log("B")
        await "c"
        console.log("C")
    }

    async function test() {
        console.log(1)
        await fun()
        console.log(3)
    }

    test()

    new Promise(resolve => {
        console.log(4)
        resolve()
    }).then(() => {
        console.log(5)
    }).then(() => {
        console.log(6)
    }).then(() => {
        console.log(7)
    })

    console.log(8)
</script>

答案是:1 2 4 8 A 5 B 6 C 7 3

再来看以下代码,输出什么?

<script>
    async function fun() {
        console.log(2)

        return new Promise(resolve => {
            resolve()
        }).then(() => {
            console.log("A")
        }).then(() => {
            console.log("B")
        }).then(() => {
            console.log("C")
        })
    }

    async function test() {
        console.log(1)
        await fun()
        console.log(3)
    }

    test()

    new Promise(resolve => {
        console.log(4)
        resolve()
    }).then(() => {
        console.log(5)
    }).then(() => {
        console.log(6)
    }).then(() => {
        console.log(7)
    })

    console.log(8)
</script>

没错,和上面用 await 写的,输出结果一模一样

Note:

awaitPromise.prototype.then 虽然很多时候可以在时间顺序上能等效(如 fun 中的 await),但是它们之间有本质的区别(如 test 中的 await)。

  • test 函数中的 await 会等待 fun 函数中所有的 await 取得 恢复函数执行 的命令并且整个函数执行完毕后才能获得取得 恢复函数执行 的命令;
  • test 函数的 await 此时不能在时间的顺序上等效 then,而要等待到 func 函数完全执行完毕;
  • 比如这里的数字 3 很晚才输出,如果单纯看成 then 的话,在下一个微任务队列执行时 3 就应该作为同步代码输出了才对。

参考 你不知道的 async、await 魔鬼细节