【问题标题】:Why does the Promise object block rendering?为什么 Promise 对象会阻塞渲染?
【发布时间】:2019-04-02 07:32:48
【问题描述】:

我正在测试 Promise 对象并编写了一些代码来模拟长时间运行的同步任务。我在比较 Promise 和 setTimeout - 请参阅 fiddle:

<!DOCTYPE html>
<html>

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

  <body>
    <h2>Promise vs setTimeout</h2>
    <div><button id="settimeout-test">setTimeout with slow running function</button></div>
    <div><button id="promise-test">Promise and slow running function</button></div>
    <div><button id="clear">Clear Results</button></div>
    <h5>Results</h5>
    <div id="result"></div>

    <script>
        const slow = function() {
            let nu = Date.now();
            while (Date.now() - nu < 1000) {}
        }
        const getSlowPromise = () => new Promise(resolve => {
                slow();
                resolve();
        });
        const resultsElement = document.getElementById('result')
        const log = (message) => {
            resultsElement.innerText += message;
        }  

        const settimeoutButton = document.getElementById('settimeout-test');
        settimeoutButton.addEventListener('click', () => {
            const now = Date.now();
            log(`\nsetTimeout test starts after ${Date.now() - now} ms`);
            setTimeout(() => {
                slow();
                log(`\nSlow function completes after ${Date.now() - now} ms`);
            }, 0);
            log(`\nEvent listener completes after ${Date.now() - now} ms`);
        });

        const promiseButton = document.getElementById('promise-test');
        promiseButton.addEventListener('click', () => {
            const now = Date.now();
            log(`\nsetTimeout test starts after ${Date.now() - now} ms`);
            getSlowPromise().then(res => log(`\nPromise completes after ${Date.now() - now} ms`));
            log(`\nevent listener completes after ${Date.now() - now} ms`);
        })

        const clear = () => resultsElement.innerText = '';
        const clearButton = document.getElementById('clear');
        clearButton.addEventListener('click', () => clear());

    </script>

  </body>

</html>

我认为 Promise 和 setTimeout 的行为方式类似,将代码添加到任务队列中,然后继续执行。结果的顺序是相同的,但是长时间运行的任务的承诺似乎会阻止渲染,直到长时间运行的任务完成。有人可以解释一下吗?

该示例在 Chrome 中运行得最好。

更新: 我不想让长时间运行的任务并行运行,我只是想了解为什么 Promise 和 setTimeout 在我的示例中表现不同。但是,如果您确实想并行运行任务,那么 Web Worker / Worker 线程就是 Quentin 建议的方式。

但我的问题的答案似乎是,正如 Bergi 在评论中所写的那样,Promise 构造函数是同步的。 这是一个更长的explanation

【问题讨论】:

  • 因为 while (Date.now() - nu &lt; 1000) {} 会阻止渲染 - Promise 不会像那样神奇地消除同步紧密循环
  • setTimeout enqueued 调用也运行在事件循环的不同部分,而不是像Promise.then 调用这样的微任务。虽然这不是您的示例代码的问题,但如果您希望它们在所有情况下都是等效的,则会给您带来问题。
  • The new Promise constructor callback runs immediately。您正在寻找与setTimeout(slow, 0) 相似的Promise.resolve().then(slow)
  • 谢谢@Bergi,这是我一直在寻找的答案。如果您将其发布为答案,我会接受。

标签: javascript promise settimeout es6-promise


【解决方案1】:

while (Date.now() - nu &lt; 1000) {} 不会模拟长时间运行的同步任务。它一个长期运行的同步任务。

Promises 是一种管理异步代码的方法,不是一种使代码异步的方法,也不是一种模拟多线程的方法。

为此,您需要查看 workers

【讨论】:

  • 你说得对,这是一个长期运行的任务,我只是说它模拟了一个更有意义的任务。我认为代码是异步的,因为它实际上是在两种情况下运行 Promise 或 setTimeout 中的代码之前运行对 log 函数的最后一次调用。但它当然不会并行运行,因为 javascript 是单线程的。但我并不想并行运行,我只是想了解为什么 Promise 和 setTimeout 的行为不同。
猜你喜欢
  • 1970-01-01
  • 2012-08-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-16
  • 1970-01-01
相关资源
最近更新 更多