为什么同步睡眠功能不会因为在承诺中而变得异步?

Why is synchronous sleep function not made async by being inside promise?

我正在努力思考 promises,以及 JavaScript 如何使用它的队列和事件循环等。

我想如果我把一个慢速同步函数放在一个promise中,那个慢速同步函数就会被委托给后台,我可以在完成后使用.then来处理它。

function syncSleep(ms){
    var end = new Date().getTime() + ms;
    var start = new Date().getTime();

    while (start < end) {
      start = new Date().getTime();
    }
}

function p() {
  return new Promise(function(resolve) {
     syncSleep(5000);
     resolve("syncSleep done!");
  });
}

p().then( function(s) {
  var div = document.getElementById('async');
  div.innerHTML = s;
} );

var div = document.getElementById('sync');
div.innerHTML = "This should appear right away! (but it doesn't)";

https://jsfiddle.net/7mw6m2x5/

尽管此代码运行时 UI 没有响应。

所以我想知道,有人可以解释一下这是怎么回事吗? Promises 只是一种处理已经 "made to be" 异步代码的方法吗?

(如果是,那是怎么做到的?)

当我不希望它冻结 UI 时如何处理慢速同步代码?我必须为此使用网络工作者吗?

感谢任何澄清。谢谢

代码按预期运行。

由于 Javascript 是单线程的,因此 UI 将在您的循环执行时阻塞。

Promises 是处理异步代码的好方法。在这里查看介绍:

http://www.html5rocks.com/en/tutorials/es6/promises/

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

为了能够在后台执行其他代码时保持 UI 响应,您需要使用 WebWorkers:

https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers

引用上面列出的页面:

"Web Workers provide a simple means for web content to run scripts in background threads. The worker thread can perform tasks without interfering with the user interface."

更新

根据您的意见,我编写了这个小脚本来演示阻塞和非阻塞方法之间的区别。 代码中有一些重复,但我认为它足够简单易懂。

function setVal(s) {
  var divAsync = document.getElementById('async');
  var innerDiv = document.createElement('div');
  innerDiv.innerHTML = s + '<br>';
  divAsync.appendChild(innerDiv);
}


function syncSleep(ms) {
  var end = new Date().getTime() + ms;
  var now = new Date().getTime();
  var stepBegin = new Date().getTime();
  var step = 0;

  // This loop is blocking
  // The UI will only refresh after the loop completion
  while (now < end) {
    now = new Date().getTime();
    step = now - stepBegin;
    if (step >= 1000) {
      setVal(now);
      step = 0;
      stepBegin = now;
    }
  }

}

function pBlock() {
  return new Promise(function(resolve) {
    syncSleep(3200);
    resolve("pBlock syncSleep done!");
  });
}


function noBlockUpdate(ms, resolve) {
  var end = new Date().getTime() + ms;
  var now = new Date().getTime();
  var stepBegin = new Date().getTime();
  var step = 0;

  function noBlock() {
    now = new Date().getTime();

    // paint every 1000ms;
    step = now - stepBegin;
    if (step >= 1000) {
      setVal(now);
      step = 0;
      stepBegin = now;
    }

    if (now < end) {
      // NB: this is going to be called thousands of times
      // But the UI will still update every 1000 ms
      setTimeout(noBlock);
    } else {
      resolve("pNoBlock done!");
    }
  };
  noBlock();
}

function pNoBlock() {
  return new Promise(function(resolve) {
    noBlockUpdate(3200, resolve);
    setVal("pNoBlock launched!");
  });
}



pNoBlock().then(setVal);

var divSync = document.getElementById('sync');
divSync.innerHTML = "This appears right away!";



// Just wait 4 seconds so the non-blocking code completes
setTimeout(function() {
  // Clear all div's
  document.getElementById('sync').innerHTML = '';
  document.getElementById('async').innerHTML = '';

  var divSync = document.getElementById('sync');
  divSync.innerHTML = "This does not appear right away, only after the blocking operation is complete!";

  pBlock().then(setVal);
}, 4000);
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>

<body>

  <div id="sync"></div>

  <div id="async"></div>

</body>

</html>

承诺不是线程。它们只是在单线程代码中处理成功和失败事件(回调)的糖。

new Promise(cb) 构造函数的回调立即同步执行。给 .then(cb)/.catch(cb) 的回调在承诺为 resolved/rejected 后的下一个 tick 执行,但它们也在同一个线程上 运行 执行。

Are Promises only a way to handle code that is already 'made to be' async?

是的。 Promises 不会真正创建异步操作。他们的目的只是通过定义一致的 API 来简化异步操作的工作。但是,它们不会避免在 resolve().then() 回调被调用之间的一小段延迟之外自行阻塞。

提供给 Promise 构造函数的函数被立即同步调用。而且,一旦 .then() 回调开始执行,它必须 运行 在引擎执行任何其他操作之前完成。

How do I deal with slow sync code when I don't want it to freeze the UI?

尽量避免long-运行同步操作。 syncSleep() 的异步替代方案是 setTimeout().

function p() {
  return new Promise(function(resolve) {
    setTimeout(function () {
      resolve("syncSleep done!");
    }, 5000);
  });
}

如果无法避免长运行 同步操作,那么您会想尝试将其移动到引擎的单独进程/实例中——在浏览器中,这可以完成 Web Workers。

一个可能的中间立场可能是,如果您可以将操作分成多个步骤,用简短的 setTimeout() 分隔每个步骤。这些中断将使引擎有时间定期处理其他事件,包括为用户更新页面。你会希望每一步都小,以便尽可能多地休息,因为一旦开始,每一步仍然会阻塞其他所有步骤。