为什么同步睡眠功能不会因为在承诺中而变得异步?
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()
分隔每个步骤。这些中断将使引擎有时间定期处理其他事件,包括为用户更新页面。你会希望每一步都小,以便尽可能多地休息,因为一旦开始,每一步仍然会阻塞其他所有步骤。
我正在努力思考 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()
分隔每个步骤。这些中断将使引擎有时间定期处理其他事件,包括为用户更新页面。你会希望每一步都小,以便尽可能多地休息,因为一旦开始,每一步仍然会阻塞其他所有步骤。