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

蓝咒 提交于 2019-12-01 21:58:57

The code works as expected.

Since Javascript is single threaded, the UI will block while your loop is executing.

Promises are just a nice way to handle async code. See an introduction here:

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

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

To be able to maintain the UI reponsive while other code is executing in the background, you would need to use WebWorkers:

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

Quoting from the page listed above:

"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."

Update:

following your comments, I crafted this little script to demonstrate the difference between blocking and no-blocking approaches. There is some repetition in the code, but I think it is simple enough to understand.

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>

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

Yes. Promises don't truly create asynchronous operations. Their intention is just to make working with asynchronous operations easier, by defining a consistent API. But, they don't avoid blocking on their own beyond one small delay between when resolve() and .then() callbacks are invoked.

The function provided to the Promise constructor is invoked immediately and synchronously. And, once a .then() callback begins executing, it will have to run to completion before the engine does anything else.

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

Try to avoid long-running synchronous operations as much as possible. An asynchronous alternative to syncSleep() would be setTimeout().

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

If a long-running synchronous operation can't be avoided, then you'll want to try to move it to a separate process / instance of the engine – and, in browsers, that can be done with Web Workers.

A possible middle ground may be, if you can break up the operation into multiple steps, to separate each step by a brief setTimeout(). Those breaks will give the engine time periodically to work on other events, including updating the page for the user. You'll want each step to be small to have as many breaks as often as possible, since each step will still block everything else once it starts.

Promises are not threads. They're just a sugar for handling success and failure events (callbacks) in single-threaded code.

Callback to new Promise(cb) constructor is executed immediately and synchronously. Callbacks given to .then(cb)/.catch(cb) are executed on next tick after the promise is resolved/rejected, but they also run on the same—and only—thread.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!