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

rb6*_*612 9 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
    }
Run Code Online (Sandbox Code Playgroud)

当我运行它时,我得到:

Print 1
Done with async work
Print 2
Run Code Online (Sandbox Code Playgroud)

我希望它是:

Print 1
Print 2
Done with async work
Run Code Online (Sandbox Code Playgroud)

我认为它将打印后一个序列,因为我宣布了该函数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)
}
Run Code Online (Sandbox Code Playgroud)

我想念什么?

T.J*_*der 11

您看到的原因有两个:

  1. 一个async函数一直同步到第一个await或第一个return,直到整个函数运行,然后返回您的情况。

  2. 忙等待不是异步的。

  3. testawait如果需要等待intense完成才能继续使用。

  4. 将某些东西变成承诺并不能使它脱颖而出。在大多数JavaScript环境(包括浏览器)中,这样做的唯一方法是使用Worker线程(MDNNode.js文档  -Node.js Worker自〜v10.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();
Run Code Online (Sandbox Code Playgroud)
.as-console-wrapper {
  max-height: 100% !important;
}
Run Code Online (Sandbox Code Playgroud)


¹有一个小警告:传递给thencatch或的处理程序finally将始终被异步调用,即使您要调用它们的诺言已经得到解决。从字面上看,这实际上是使异步成为唯一的承诺。


Jon*_*lms 5

因此它将立即返回promise,并继续异步工作。

不,它不会。传递给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);
Run Code Online (Sandbox Code Playgroud)

*如果您打算进行真正的“强烈”工作,那么在另一个线程中进行操作可能会有所帮助(请参阅WebWorker浏览器和child_process.spawnNodeJS)。