为什么`after resolve`在`after wait`之前执行?

4

代码片段如下:

console.log('test')

async function async1() {
    console.log('before wait');
    await wait();
    console.log('after wait');
}

async function wait() {
    try {
        return await new Promise((res, rej) => {
            console.log('wait');
            rej('reject');
        });
    } catch (reason) {
        console.log(reason);
    }
}

async function test() {
    async1();
    console.log('before resolve');
    await Promise.resolve();
    console.log('after resolve');
}

test();

console.log('test end');

你也可以在playground中运行代码。

实际结果:

[LOG]: "test" 
[LOG]: "before wait" 
[LOG]: "wait" 
[LOG]: "before resolve" 
[LOG]: "test end" 
[LOG]: "reject" 
[LOG]: "after resolve" 
[LOG]: "after wait" 

我以为 after wait 会先被打印出来,因为它早就被推入了微任务队列中。

我错在哪里?


我向@Seblor寻求了建议

如果你用then函数替换async/await,可能会更清晰。

然后我移除了所有的async/await。

console.log('test')

function async1() {
    console.log('before wait');
    return wait().then(() => {
        console.log('after wait');
    })
}

function wait() {
    return new Promise((res, rej) => {
        console.log('wait');
        rej('reject');
    }).catch((reason) => {
        console.log(reason);
    })
}

function test() {
    async1();
    console.log('before resolve');
    Promise.resolve().then(() => {
        console.log('after resolve');
    })
}

test();

console.log('test end');

然后我意识到,第一个被推入微任务队列的代码是位于 catch 中的代码,其次是 console.log('after resolve'),最后是 console.log('after wait')


你为什么在test函数中调用async1()?你是不是想要对它进行await操作? - Bergi
1
“我原以为“wait之后”会首先被打印,因为它早在微任务队列中被推入了?”,不是的,第一个被推入微任务队列的代码是记录“reject”的代码。 - Bergi
@Bergi,我不是想使用await,而是在我的工作中遇到了一个类似的情况,所以我只是随意编写了async1来模拟我遇到的场景。“推送到微任务队列的第一段代码是记录“reject”的代码”这让我茅塞顿开,谢谢。 - YJ Cai
2个回答

3

好的,我花了一些时间才找到问题所在,但现在我觉得我懂了。

我们都同意问题出在哪里了。你看到的问题是,在解决 Promise 之前,Promise 拒绝被执行,但“在解决之后”被打印在“等待之后”之前。

问题在于,虽然有 2 个 Promises,但这里有3 个微任务。第一个是 return await new Promise(,第二个是 await Promise.resolve(),第三个是对函数调用的等待:await wait()

wait() 的调用返回一个 Promise,你使用 await 等待它,从而创建了一个新的微任务,排在 await Promise.resolve() 之后。

因此,拒绝的 Promise 已经完成,打印出“reject”,接着是解决 Promise,打印出“resolve”,最后等待来自 wait() 的返回 Promise,打印出“after wait”。

如果你将 async/await 替换为 then 函数,可能会更清楚。所以,这个:

async function async1() {
    console.log('before wait');
    await wait();
    console.log('after wait');
}

会写成这样:

function async1() {
    console.log('before wait');
    return wait().then(() => {
        console.log('after wait');
    });
}

因此,按照执行顺序(仅针对微任务):

  1. await new Promise((res, rej) => { 注册第一个Promise
  2. await Promise.resolve() 注册第二个Promise
  3. 第一个Promise被拒绝,打印'reject'
  4. 由于第一个promise已返回,我们可以最终注册'then'回调,创建第三个微任务。
  5. 第二个Promise被解决,因为这是同步代码,所以会打印'after resolve'
  6. 执行第三个微任务,打印'after wait'

不仅仅只有两个承诺,还有更多。 - Bergi
用 then 函数替换 async/await 确实更清晰!真的很有帮助~ - YJ Cai

-3
在给定的代码片段中,它并不等待Promise.resolve()返回任何响应,因为它没有等待任何操作。为了摆脱这个问题,我们可以简单地使用函数链式调用,例如:

async function async3() {
    return new Promise(async(res, rej)=>{
        console.log('before wait async3');
        await wait();
        console.log('after wait async3');
        res()
    })
}
    
async function async2() {
    return new Promise(async(res, rej)=>{
        console.log('before wait async2');
        await wait();
        console.log('after wait async2');
        res()
   })
}
    
async function async1() {
    return new Promise(async(res, rej)=>{
         console.log('before waitasync1 ');
         await wait();
         console.log('after waitasync1');
         res()
    })
}
    
async function wait() {
    try {
        return await new Promise((res, rej) => {
            console.log('wait');
            rej('reject');
        });
    } catch (reason) {
        console.log(reason);
    }
}
    
async function test() {
    await async1();
    await async2();
    await async3();
}
    
test();


1
不要将 async 函数作为执行器传递给 new Promise - Bergi
请修复代码的缩进,这样答案就很难阅读了。 - Bergi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接