我正在努力思考 promises,以及 JavaScript 如何使用它的队列和事件循环等。
我想如果我把一个慢速同步函数放在一个 promise 中,那个慢速同步函数就会被委托(delegate)给后台,我可以在它完成时使用 .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 只是一种处理已经“成为”异步代码的方法吗?
(如果是,那是怎么做到的?)
当我不希望它卡住 UI 时,如何处理慢速同步代码?我必须为此使用网络 worker 吗?
感谢任何澄清。谢谢。
代码按预期工作。
由于 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 为 Web 内容提供了一种在后台线程中运行脚本的简单方法。工作线程可以在不干扰用户界面的情况下执行任务。”
更新:
根据您的意见,我编写了这个小脚本来演示阻塞方法和非阻塞方法之间的区别。
代码中有一些重复,但我认为它足够简单易懂。
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>