async await 在浏览器中真的是非阻塞的吗?
Is async await truly non-blocking in the browser?
我一直在使用 TypeScript 和本机 Promises 研究 SPA 中的功能,我注意到即使我将一个 long-运行 函数重构为返回一个 promise 的异步函数,UI 仍然没有反应。
所以我的问题是:
新的 async/await 功能如何帮助避免在浏览器中阻止 UI?在使用 async/await 实际获得响应式 UI 时,是否需要采取任何特殊的额外步骤?
有人可以创建一个 fiddle 来演示 async/await 如何帮助 UI 响应吗?
async/await 与先前的异步功能(如 setTimeout 和 XmlHttpRequest)有何关系?
async
是构建异步代码的更优雅的方式。它不允许任何新功能;它只是一种比回调或承诺更好的语法。
所以,async
不能用来"make something asynchronous"。如果您的代码必须执行大量基于 CPU 的处理,async
不会神奇地使 UI 响应。您需要做的是使用 web workers 之类的东西, 是 将 CPU 绑定的工作推送到后台线程的合适工具,以便UI 反应灵敏。
await p
安排在 promise p
解析时执行其余函数。就这些了。
async
让您可以使用 await
。这就是(几乎)它所做的一切(它还将您的结果包装在一个承诺中)。
它们一起使非阻塞代码读起来像更简单的阻塞代码。他们不解锁代码。
对于响应式 UI,将 CPU 密集型工作卸载到 worker 线程,并向其传递消息:
async function brutePrime(n) {
function work({data}) {
while (true) {
let d = 2;
for (; d < data; d++) {
if (data % d == 0) break;
}
if (d == data) return self.postMessage(data);
data++;
}
}
let b = new Blob(["onmessage =" + work.toString()], {type: "text/javascript"});
let worker = new Worker(URL.createObjectURL(b));
worker.postMessage(n);
return await new Promise(resolve => worker.onmessage = e => resolve(e.data));
}
(async () => {
let n = 700000000;
for (let i = 0; i < 10; i++) {
console.log(n = await brutePrime(n + 1));
}
})().catch(e => console.log(e));
JavaScript 是单线程的,运行 与 UI 在同一个线程中。所以所有 JavaScript 代码都会阻塞 UI。正如其他人提到的,网络工作者可用于 运行 其他线程中的代码,但它们有局限性。
异步函数和常规函数之间的区别在于它们 return 一个承诺。使用回调,您可以延迟代码的执行,该代码处理函数调用的结果,从而允许 UI 做一些工作。以下三个例子效果相同:
async function foo() {
console.log("hi");
return 1;
}
foo().then(result => console.log(result))
console.log("lo");
function foo() {
console.log("hi");
return 1;
}
Promise.resolve(foo()).then(result => console.log(result))
console.log("lo");
function foo() {
console.log("hi");
return 1;
}
const result = foo();
setTimeout(() => console.log(result));
console.log("lo");
在所有三种情况下,控制台都会记录 hi、lo、1。在打印 1 之前,UI 可以处理用户输入或绘图更新。前两种情况最后打印 1 的原因是 promise 的回调没有立即执行。
await
允许您在没有回调的情况下做到这一点:
async function foo() {
console.log("hi");
return 1;
}
async function bar() {
const result = await foo();
console.log(result);
}
bar();
console.log("lo");
这也会打印出 hi, lo, 1。就像承诺的回调一样,await
之后的代码永远不会立即执行。
从developer.mozilla.org上的描述可以看出是non-blocking:
The await keyword causes the JavaScript runtime to pause your code on
this line, allowing other code to execute in the meantime (Note: my bold), until the
async function call has returned its result. Once that's complete,
your code continues to execute starting on the next line.
我来晚了。我想验证同步和异步用例。
需要说明的是,async/await 不会创建同步代码。它只添加 syntactic sugar
使代码看起来同步。在包装器下,Promise 逻辑继续实现 non-preemptive multitasking
.
在示例 gist
中,您可以 运行 使用命令行参数选择阻塞或非阻塞 CLI 输入的示例。
asyncExample.js
我一直在使用 TypeScript 和本机 Promises 研究 SPA 中的功能,我注意到即使我将一个 long-运行 函数重构为返回一个 promise 的异步函数,UI 仍然没有反应。
所以我的问题是:
新的 async/await 功能如何帮助避免在浏览器中阻止 UI?在使用 async/await 实际获得响应式 UI 时,是否需要采取任何特殊的额外步骤?
有人可以创建一个 fiddle 来演示 async/await 如何帮助 UI 响应吗?
async/await 与先前的异步功能(如 setTimeout 和 XmlHttpRequest)有何关系?
async
是构建异步代码的更优雅的方式。它不允许任何新功能;它只是一种比回调或承诺更好的语法。
所以,async
不能用来"make something asynchronous"。如果您的代码必须执行大量基于 CPU 的处理,async
不会神奇地使 UI 响应。您需要做的是使用 web workers 之类的东西, 是 将 CPU 绑定的工作推送到后台线程的合适工具,以便UI 反应灵敏。
await p
安排在 promise p
解析时执行其余函数。就这些了。
async
让您可以使用 await
。这就是(几乎)它所做的一切(它还将您的结果包装在一个承诺中)。
它们一起使非阻塞代码读起来像更简单的阻塞代码。他们不解锁代码。
对于响应式 UI,将 CPU 密集型工作卸载到 worker 线程,并向其传递消息:
async function brutePrime(n) {
function work({data}) {
while (true) {
let d = 2;
for (; d < data; d++) {
if (data % d == 0) break;
}
if (d == data) return self.postMessage(data);
data++;
}
}
let b = new Blob(["onmessage =" + work.toString()], {type: "text/javascript"});
let worker = new Worker(URL.createObjectURL(b));
worker.postMessage(n);
return await new Promise(resolve => worker.onmessage = e => resolve(e.data));
}
(async () => {
let n = 700000000;
for (let i = 0; i < 10; i++) {
console.log(n = await brutePrime(n + 1));
}
})().catch(e => console.log(e));
JavaScript 是单线程的,运行 与 UI 在同一个线程中。所以所有 JavaScript 代码都会阻塞 UI。正如其他人提到的,网络工作者可用于 运行 其他线程中的代码,但它们有局限性。
异步函数和常规函数之间的区别在于它们 return 一个承诺。使用回调,您可以延迟代码的执行,该代码处理函数调用的结果,从而允许 UI 做一些工作。以下三个例子效果相同:
async function foo() {
console.log("hi");
return 1;
}
foo().then(result => console.log(result))
console.log("lo");
function foo() {
console.log("hi");
return 1;
}
Promise.resolve(foo()).then(result => console.log(result))
console.log("lo");
function foo() {
console.log("hi");
return 1;
}
const result = foo();
setTimeout(() => console.log(result));
console.log("lo");
在所有三种情况下,控制台都会记录 hi、lo、1。在打印 1 之前,UI 可以处理用户输入或绘图更新。前两种情况最后打印 1 的原因是 promise 的回调没有立即执行。
await
允许您在没有回调的情况下做到这一点:
async function foo() {
console.log("hi");
return 1;
}
async function bar() {
const result = await foo();
console.log(result);
}
bar();
console.log("lo");
这也会打印出 hi, lo, 1。就像承诺的回调一样,await
之后的代码永远不会立即执行。
从developer.mozilla.org上的描述可以看出是non-blocking:
The await keyword causes the JavaScript runtime to pause your code on this line, allowing other code to execute in the meantime (Note: my bold), until the async function call has returned its result. Once that's complete, your code continues to execute starting on the next line.
我来晚了。我想验证同步和异步用例。
需要说明的是,async/await 不会创建同步代码。它只添加 syntactic sugar
使代码看起来同步。在包装器下,Promise 逻辑继续实现 non-preemptive multitasking
.
在示例 gist
中,您可以 运行 使用命令行参数选择阻塞或非阻塞 CLI 输入的示例。
asyncExample.js