I'm trying to wrap my head around promises, and how JavaScript works with it's queue and event loop etc.
I thought that if I put a slow synchronous function inside a promise, that slow sync function would be delegated to the background and I could use .then
to deal with it when it was done.
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/
The UI is unresponsive while this code runs though.
So I was wondering, can someone explain what is going on here? Are Promises only a way to handle code that is already "made to be" async?
(If so, how is that done?)
How do I deal with slow sync code when I don't want it to freeze the UI? Do I have to use a web worker for that?
Grateful for any clarification. Thanks.
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>