【问题标题】:Promise#all vs sequential awaits - difference in time resolutionPromise#all 与顺序等待 - 时间分辨率的差异
【发布时间】:2019-01-13 20:18:56
【问题描述】:

在下面的 sn-p 中,当使用 promise#all 并为每个 promise 依次等待时,解决时间是不同的。我想深入了解发生了什么,因为在处理性能时这似乎是一个警告。

*编辑:添加了另一个没有函数调用的情况,可能我错过了等待如何工作的东西

// Code goes here

window.onload = function() {
  
    let p = new Promise((res, rej) => {
        setTimeout(() => {
            res('p OK')
        }, 5000);
    })
   
    let p2 = new Promise((res, rej) => {
        setTimeout(() => {
            res('p2 OK')
        }, 5000);
    })
    
    let p3 = new Promise((res, rej) => {
        setTimeout(() => {
            res('p3 OK')
        }, 5000);
    })
  
    async function pp() {
        return new Promise((res, rej) => {
            setTimeout(() => {
                res('pp OK');
            }, 5000);
        });
    }
  
    async function a() {
        let out = await Promise.all([p, pp()]);
        return `#a ${out}`;
    }
  
    async function b() {
       let out1 = await p;
       let out2 = await pp();
       return `#b ${out1} ${out2}`;
    }
    
    async function c() {
        let out1 = await p;
        let out2 = await p2;
        let out3 = await p3;
        return `#c ${out1} ${out2} ${out3}`;
    }
  
    let out1 = document.getElementById("out1");
    let out2 = document.getElementById("out2");
    let out32 = document.getElementById("out2");
    const date = new Date().getSeconds();
    a().then(x => console.log(x)).then(() => out1.innerHTML += `finished after ${new Date().getSeconds() - date}s`);
    b().then(x => console.log(x)).then(() => out2.innerHTML += `finished after ${new Date().getSeconds() - date}s`);
    c().then(x => console.log(x)).then(() => out3.innerHTML += `finished after ${new Date().getSeconds() - date}s`);

}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!DOCTYPE html>
<html>

<head>
    <link rel="stylesheet" href="style.css">
    <script src="script.js"></script>
</head>

<body>
    <p id="out1">
        Promise#all  
    </p>
    <p id="out2">
        Sequential awaits
    </p>
    <p id="out3">
        Sequential awaits without executing fnc
    </p>
</body>

</html>

【问题讨论】:

标签: javascript time promise async-await


【解决方案1】:

一些可以澄清事情的点:

  • 传递给new Promise的构造函数回调函数立即执行,这意味着setTimeout延迟在那个时刻开始,无论你是等待与否。

  • 因此,p、p2 和 p3 创建的超时都会在这些变量初始化后立即启动。由于它们都将在 5 秒后超时,因此相应的承诺(p、p2 和 p3)将在大约同一时间得到解决。同样,这独立取决于您是使用await、then、Promise.all,还是忘记这些承诺而不用它们做任何事情。

  • 相比之下,函数pp只是一个函数。这不是一个承诺。只有当您真正调用该函数时,您才会创建一个带有相应超时的promise。

  • await 使async 函数返回立即,返回一个承诺。这意味着函数的其余代码被推迟,而不是其他 Javascript 代码的其余部分:函数返回并在该函数调用后继续执行。当调用堆栈为空时,将处理不同的事件队列。所以它没有阻塞。一旦传递给await 的promise 解决了,神奇的事情就会发生:async 的函数执行上下文被恢复,并且继续执行直到下一个await。如果没有更多的await 可以执行,并且函数结束,它返回的promise(当它遇到第一个await 时)resolved。

  • Promise.all 不会影响单个承诺何时解决。它会创建一个新的 Promise,当 all 给定的 Promise 已解决时,该 Promise 会解决。如上所述,您的情况下的承诺(p,p2)大约在同一时刻解决,因此Promise.all 也将在大约 5 秒后解决。

  • 如果您执行await pp(),您只会在当时创建该承诺,而不是更早。而且因为前面有一个await p,所以在执行pp() 之前需要5 秒钟。所以相应的超时不会开始直到第二个await被执行。这就是await p; await pp() 需要两次 5 秒才能解决的原因。

  • await p; await p2 的情况有所不同。两个 Promise 都已经创建,并且它们的超时时间已经开始。所以当5秒后第一个await结束时,JS会到第二个await,会发现p2也解决了。不会有任何额外的等待时间(除了异步管理,await 总是将事件放入微任务队列)

我认为通过这些元素,您可以正确地描绘代码将如何执行,以及您获得的输出结果如何。

【讨论】:

    【解决方案2】:

    在第一个函数中,它立即执行pp 以启动承诺超时。 在第二个函数中,它会等到p 完成后才执行pp 来启动promise timeout。

    await 关键字“阻止*”执行,直到 promise 解决,然后继续下一行。

    RE:更新

    在您的第三个示例中,所有的承诺都是在当时开始的。 await 关键字等到 promise 被解决。因为你所有的 Promise 都是同时开始的,所以它们都会在一个很小的增量内解决。当您点击await p2 时,它可能已经解决了。 p3 也一样。

    在您的第二个示例中,pp 在p 完成之前不会启动。

    [*] 它并没有真正阻止执行,但这就是它对您的代码的影响。

    【讨论】:

    • 你能检查一下更新的sn-p吗?我已经包含了一个函数 c 等待 3 个承诺,这些承诺也会在 5 秒后返回。它不会以任何方式被阻止。谢谢
    • 能否请您详细说明为什么上一个示例中的promise是同时启动的?
    • 因为它们都是同时创建的。 pp下的promise只在函数调用时创建。
    猜你喜欢
    • 2021-06-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-17
    • 1970-01-01
    • 2016-03-21
    • 2014-12-15
    相关资源
    最近更新 更多