我试图更好地掌握JS中的异步功能和诺言。为此,我编写了一个示例程序,其目标是调用一个可以完成繁忙工作的函数(有意不使用异步setTimeout
,因为我想模仿长时间运行的进程),但是立即返回。但是,我似乎无法弄清楚为什么它不起作用。
test();
async function intense(){
var start = new Date().getTime();
for (var i = 0; i < 1e6; i++) {
if ((new Date().getTime() - start) > 2000){
break;
}
}
console.log("Done with async work");
}
async function test(){
console.log("Print 1");
intense(); // does some busy work for a few seconds
console.log("Print 2"); // want this to print immediately after print 1
}
运行它,我得到:
Print 1
Done with async work
Print 2
我希望是这样
Print 1
Print 2
Done with async work
我认为它将打印后面的序列,因为我宣布了函数intense()
是异步的,因此它将立即返回一个promise并继续异步工作。
我什至试图将intense
函数重构为可以立即解决但无济于事的承诺。
async function intense(){
return new Promise((resolve)=> {
resolve();
var start = new Date().getTime();
for (var i = 0; i < 1e6; i++) {
if ((new Date().getTime() - start) > 2000){
break;
}
}
console.log("Done with async work");
}, null)
}
我想念什么?
答案 0 :(得分:10)
您看到的原因有两个:
async
函数是同步的,直到第一个await
或return
为止,因此整个函数在返回您的情况之前会运行。 / p>
忙等待不是异步的。
test
如果要等待await
完成然后再继续,则需要使用intense
。
将某些内容变为诺言不会使它脱颖而出。在大多数JavaScript环境(包括浏览器)中,这样做的唯一方法是使用Worker
线程(MDN,Node.js docs-自〜v10以来,Node.js拥有Worker
.5,并且仍标记为“实验性”,因此该API的主要部分应该相当稳定,因为它们是从网络工作者标准API中提取的。)
重要的是要记住,promise不会使任何异步内容¹,它们提供了一种观察已经已经异步的结果的方法。
下面是使用setTimeout
作为异步部分的示例,它可以帮助您更好地理解这些内容:
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function intense(value) {
console.log("intense(" + value + ") - This is synchronous");
await delay(100);
console.log("intense(" + value + ") - This is asynchronous, because it's after `await`");
}
async function test(){
console.log("Print 1");
intense(1); // <== WITHOUT await
console.log("Print 2");
await intense(2); // <== With await
console.log("Print 3");
}
test();
.as-console-wrapper {
max-height: 100% !important;
}
¹有一个小警告:传递给then
,catch
或finally
的处理程序将始终被异步调用,即使您承诺在其上调用它们已经解决了从字面上看,这实际上是唯一的承诺,使之异步。
答案 1 :(得分:5)
因此它将立即返回一个诺言并继续异步工作。
不,不会。传递给Promise构造函数的回调被立即调用。异步是指稍后调用resolve
或reject
的过程,以及.then
链如何在某个时候被回调的情况。
但是,从代码在另一个线程上运行或被延迟的角度来看,它不是异步的,因为JS本身是在单个线程中执行的,所以不会发生*。
console.log(1);
const promise = new Promise((resolve, reject) => {
console.log(2); // gets executed immeadiately
});
promise.then(() => console.log(4)); // < Promise resolve asynchronously
console.log(3);
*如果您打算进行真正的“高强度”工作,那么在另一个线程中进行操作可能会有所帮助(请参阅浏览器中的WebWorker
和NodeJS的child_process.spawn
)。
答案 2 :(得分:1)
这是一个误解,使用javascript很容易做到。您的函数intense()
会阻塞线程。在异步函数中放置某些内容不会改变您在javascript中只有一个线程这一事实。一旦解释器开始运行该for
循环,它将使用一个线程来运行它,直到结束为止。在那之前没有其他事情发生。
异步函数不会立即返回,它们会运行代码的主体,直到命中等待并返回承诺为止。在您的示例中,整个函数将在返回之前运行。
您不能不加阻塞地使用这种长时间运行的过程。这就是节点竭尽全力将诸如I / O访问和计时器之类的工作卸载到另一个线程的原因。
有关更多详细信息,请参见此处:https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/
如果您要异步运行一些长时间运行的代码,则需要生成一个子进程:https://nodejs.org/api/child_process.html