为什么Javascript异步功能没有立即返回?

时间:2019-03-10 09:49:15

标签: javascript promise

我试图更好地掌握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)
}

我想念什么?

3 个答案:

答案 0 :(得分:10)

您看到的原因有两个:

  1. async函数是同步的,直到第一个awaitreturn为止,因此整个函数在返回您的情况之前会运行。 / p>

  2. 忙等待不是异步的。

  3. test如果要等待await完成然后再继续,则需要使用intense

  4. 将某些内容变为诺言不会使它脱颖而出。在大多数JavaScript环境(包括浏览器)中,这样做的唯一方法是使用Worker线程(MDNNode.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;
}


¹有一个小警告:传递给thencatchfinally的处理程序将始终被异步调用,即使您承诺在其上调用它们已经解决了从字面上看,这实际上是唯一的承诺,使之异步。

答案 1 :(得分:5)

  

因此它将立即返回一个诺言并继续异步工作。

不,不会。传递给Promise构造函数的回调被立即调用。异步是指稍后调用resolvereject的过程,以及.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