如何在 Javascript 中执行非阻塞调用? (承诺不会那样做。)
How to perform a non-blocking call in Javascript? (Promises don't do that.)
在 Javascript 运行 现代浏览器中,我想对函数进行非阻塞调用。
有人向我指出 Promises 和构建在它们之上的异步函数,但我发现 Promises(以及异步函数)确实会阻塞。请参阅下面的代码来演示这一点。
唯一不阻塞的函数似乎是内置的。例如设置超时。这似乎就是为什么我发现的所有 'nonblocking' Promise 示例都使用 setTimeout。单个执行线程遍历超时代码并调用非阻塞 setTimeout 以继续步进。 Promise 本身只是组织回调,这很好,但它们本身不会导致调用成为非阻塞的。
webworker 解决方案似乎没有引用,因此无法修改调用者的数据。序列化、调用 web worker,然后反序列化以取回结果将是相当低效且复杂的。
这是从 Promise 获取 'non-blocking' 行为的典型示例,但如果您在 Web 控制台中单步执行它,您将看到唯一不会阻塞的是 setTimeout。
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
</head>
<body>
<script>
'use strict';
function wait_setTimeout(call_when_finished){
console.log("entering wait_setTimeout");
setTimeout(call_when_finished, 2000); // 3 second delay
}
function wait_setTimeout_resolve(){
console.log("wait_setTimeout_resolved");
}
console.log("before wait_setTimeout promise");
let p0 = new Promise((wait_setTimeout_resolve) => {
this.wait_setTimeout(wait_setTimeout_resolve);
});
console.log("after new Promise(wait_setTimeout_resolve)");
p0.then(() => console.log("then wait_setTimeout_promise target"));
console.log("after wait_setTimeout_promise.then");
/*
before wait_setTimeout promise
entering wait_setTimeout
after new Promise(wait_setTimeout_resolve)
after wait_setTimeout_promise.then
<delay occurs here, as it should, but because setTimeout didn't block, not the Promise>
then wait_setTimeout_promise target
*/
</script>
</body>
如果我们用循环创建时间延迟,则很明显 Promise 正在阻塞:
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
</head>
<body>
<script>
'use strict';
let limit = 100000; // ~3 second delay, make this bigger if you don't see a delay
function wait_loop(call_when_finished){
console.log("entering wait_loop");
let i = 0;
let j;
while(i < limit){
i++;
j = 0;
while(j < limit){
j++;
}}
call_when_finished();
}
function wait_loop_resolve(){
console.log("wait_loop_resolved");
}
console.log("before wait_loop promise");
let p1 = new Promise((wait_loop_resolve) => {
this.wait_loop(wait_loop_resolve);
});
console.log("after new Promise(wait_loop_resolve)");
p1.then(() => console.log("then wait_loop_promise target"));
console.log("after wait_loop_promise.then");
/*
before wait_loop promise
entering wait_loop
<delay occurs here.. i.e. the new Promise blocked>
after new Promise(wait_loop_resolve)
after wait_loop_promise.then
then wait_loop_promise target
*/
</script>
</body>
当然循环只是一个占位符。实际代码正在计算所需的东西。
PromiseConstructor
调用一个名为 executor
的参数,它必须是一个函数并且与两个参数 resolve
和 reject
.
同步执行
因此,在您的代码中,执行器 (wait_loop_resolve) => { this.wait_loop(wait_loop_resolve); }
会立即执行,这意味着您的函数 wait_loop
也会被执行。您可以使用 setTimeout(wait_loop, 0)
或 Promise.resolve().then(wait_loop)
异步执行它。如果需要传递参数,可以写成如下格式:
setTimeout(() => wait_loop(wait_loop_resolve), 0)
// or with promise
Promise.resolve(wait_loop_resolve).then(wait_loop)
Promise 不是线程,JS 非常single-threaded,并且在事件队列中工作。 Indeed 承诺只是组织回调。
如果你想 运行 CPU-intensive 代码,那么你需要使用 Web Workers,并使用他们的界面与他们交流,postMessage
(你如果您愿意,可以包装成 Promise-returning 格式)。它们确实作为脚本 运行 在单独的线程上运行,但请注意它们的通信是如何受到限制的,它不像经典多线程那样 free-reign 内存访问。
Web Workers 将无法访问您的 window,因此不会对它们进行 DOM 修改。但是,如果您有复杂的模拟,您可以巧妙地将数据模型与显示分开,并且只需将数据模型传输到 worker 或从 worker 传输数据模型,使用普通脚本将其转换为 UI.
要了解有关 JavaScript 执行模型的更多信息,请参阅 introduction on MDN, and if you want a very deep view on how that is implemented, this presentation by Jake Archibald。
我想你搞错了。
- 你要的是multi-thread,不是async。
- 异步与 multi-thread 无关,异步可以 运行 在单线程上。
在您的情况下,当您可以 then
时,您的线程已 释放 ,因此它将立即调用并等待结果,但如果您需要这样做其他诸如渲染、解码等...向用户显示加载页面!这就是异步的目的。
如果您需要 pre-do 东西。例如:pre-decoding 某种数据,pre-compute 图片等...是的,我的意思是你必须 pre
做!或者使用网络工作者。问题是您必须知道数据并在将其显示给用户之前预先计算它。
关于异步的更多信息是
在 Javascript 运行 现代浏览器中,我想对函数进行非阻塞调用。
有人向我指出 Promises 和构建在它们之上的异步函数,但我发现 Promises(以及异步函数)确实会阻塞。请参阅下面的代码来演示这一点。
唯一不阻塞的函数似乎是内置的。例如设置超时。这似乎就是为什么我发现的所有 'nonblocking' Promise 示例都使用 setTimeout。单个执行线程遍历超时代码并调用非阻塞 setTimeout 以继续步进。 Promise 本身只是组织回调,这很好,但它们本身不会导致调用成为非阻塞的。
webworker 解决方案似乎没有引用,因此无法修改调用者的数据。序列化、调用 web worker,然后反序列化以取回结果将是相当低效且复杂的。
这是从 Promise 获取 'non-blocking' 行为的典型示例,但如果您在 Web 控制台中单步执行它,您将看到唯一不会阻塞的是 setTimeout。
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
</head>
<body>
<script>
'use strict';
function wait_setTimeout(call_when_finished){
console.log("entering wait_setTimeout");
setTimeout(call_when_finished, 2000); // 3 second delay
}
function wait_setTimeout_resolve(){
console.log("wait_setTimeout_resolved");
}
console.log("before wait_setTimeout promise");
let p0 = new Promise((wait_setTimeout_resolve) => {
this.wait_setTimeout(wait_setTimeout_resolve);
});
console.log("after new Promise(wait_setTimeout_resolve)");
p0.then(() => console.log("then wait_setTimeout_promise target"));
console.log("after wait_setTimeout_promise.then");
/*
before wait_setTimeout promise
entering wait_setTimeout
after new Promise(wait_setTimeout_resolve)
after wait_setTimeout_promise.then
<delay occurs here, as it should, but because setTimeout didn't block, not the Promise>
then wait_setTimeout_promise target
*/
</script>
</body>
如果我们用循环创建时间延迟,则很明显 Promise 正在阻塞:
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
</head>
<body>
<script>
'use strict';
let limit = 100000; // ~3 second delay, make this bigger if you don't see a delay
function wait_loop(call_when_finished){
console.log("entering wait_loop");
let i = 0;
let j;
while(i < limit){
i++;
j = 0;
while(j < limit){
j++;
}}
call_when_finished();
}
function wait_loop_resolve(){
console.log("wait_loop_resolved");
}
console.log("before wait_loop promise");
let p1 = new Promise((wait_loop_resolve) => {
this.wait_loop(wait_loop_resolve);
});
console.log("after new Promise(wait_loop_resolve)");
p1.then(() => console.log("then wait_loop_promise target"));
console.log("after wait_loop_promise.then");
/*
before wait_loop promise
entering wait_loop
<delay occurs here.. i.e. the new Promise blocked>
after new Promise(wait_loop_resolve)
after wait_loop_promise.then
then wait_loop_promise target
*/
</script>
</body>
当然循环只是一个占位符。实际代码正在计算所需的东西。
PromiseConstructor
调用一个名为 executor
的参数,它必须是一个函数并且与两个参数 resolve
和 reject
.
因此,在您的代码中,执行器 (wait_loop_resolve) => { this.wait_loop(wait_loop_resolve); }
会立即执行,这意味着您的函数 wait_loop
也会被执行。您可以使用 setTimeout(wait_loop, 0)
或 Promise.resolve().then(wait_loop)
异步执行它。如果需要传递参数,可以写成如下格式:
setTimeout(() => wait_loop(wait_loop_resolve), 0)
// or with promise
Promise.resolve(wait_loop_resolve).then(wait_loop)
Promise 不是线程,JS 非常single-threaded,并且在事件队列中工作。 Indeed 承诺只是组织回调。
如果你想 运行 CPU-intensive 代码,那么你需要使用 Web Workers,并使用他们的界面与他们交流,postMessage
(你如果您愿意,可以包装成 Promise-returning 格式)。它们确实作为脚本 运行 在单独的线程上运行,但请注意它们的通信是如何受到限制的,它不像经典多线程那样 free-reign 内存访问。
Web Workers 将无法访问您的 window,因此不会对它们进行 DOM 修改。但是,如果您有复杂的模拟,您可以巧妙地将数据模型与显示分开,并且只需将数据模型传输到 worker 或从 worker 传输数据模型,使用普通脚本将其转换为 UI.
要了解有关 JavaScript 执行模型的更多信息,请参阅 introduction on MDN, and if you want a very deep view on how that is implemented, this presentation by Jake Archibald。
我想你搞错了。
- 你要的是multi-thread,不是async。
- 异步与 multi-thread 无关,异步可以 运行 在单线程上。
在您的情况下,当您可以 then
时,您的线程已 释放 ,因此它将立即调用并等待结果,但如果您需要这样做其他诸如渲染、解码等...向用户显示加载页面!这就是异步的目的。
如果您需要 pre-do 东西。例如:pre-decoding 某种数据,pre-compute 图片等...是的,我的意思是你必须 pre
做!或者使用网络工作者。问题是您必须知道数据并在将其显示给用户之前预先计算它。
关于异步的更多信息是