JavaScript 返回新的 Promise 而没有 resolve() 语句时,不会如预期般进行等待。

huangapple go评论122阅读模式
英文:

JavaScript returning new Promise without resolve() statement, doesn't await as expected

问题

我有一个非常简单的代码片段如下

    async function neverResolve() {
      return new Promise(() => {
        console.log("This promise will never resolve");
      });
    }

    (async () => {
      try {
        console.log("START");
        // neverResolve().then().catch(); // 取消注释这一行会按预期工作
        await neverResolve();
        await new Promise((resolve) => setTimeout(() => resolve(), 5000));
        console.log("END");
      } catch (error) {
        console.log("ERR: ", error);
      }
    })();

为什么上面的函数不会等待5秒钟并打印END
它在打印以下内容后自动终止:

START
This promise will never resolve

但如果我们使用.then()结构执行相同的函数,我会得到预期的结果。

    async function neverResolve() {
      return new Promise(() => {
        console.log("This promise will never resolve");
      });
    }

    (async () => {
      try {
        console.log("START");
        neverResolve().then().catch(); 
        await new Promise((resolve) => setTimeout(() => resolve(), 5000));
        console.log("END");
      } catch (error) {
        console.log("ERR: ", error);
      }
    })();
英文:

I have a very simple code snippet like this

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

async function neverResolve() {
  return new Promise(() =&gt; {
    console.log(&quot;This promise will never resolve&quot;);
  });
}

(async () =&gt; {
  try {
    console.log(&quot;START&quot;);
    // neverResolve().then().catch(); // uncommenting this line works as expected
    await neverResolve();
    await new Promise((resolve) =&gt; setTimeout(() =&gt; resolve(), 5000));
    console.log(&quot;END&quot;);
  } catch (error) {
    console.log(&quot;ERR: &quot;, error);
  }
})();

<!-- end snippet -->

Why the above function doesn't wait for 5 second and print's the END.
It automatically terminates after printing

START
This promise will never resolve

But if we execute the same function but with a .then() construct, I get the expected result.

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

async function neverResolve() {
  return new Promise(() =&gt; {
    console.log(&quot;This promise will never resolve&quot;);
  });
}

(async () =&gt; {
  try {
    console.log(&quot;START&quot;);
    neverResolve().then().catch(); 
    await new Promise((resolve) =&gt; setTimeout(() =&gt; resolve(), 5000));
    console.log(&quot;END&quot;);
  } catch (error) {
    console.log(&quot;ERR: &quot;, error);
  }
})();

<!-- end snippet -->

答案1

得分: 1

`neverResolve`既不会解析也不会拒绝所以程序会在`await`处无限期地挂起考虑将超时功能抽象成自己的通用函数`timeout` -

const sleep = ms =>
  new Promise(r => setTimeout(r, ms));

const timeout = (p, ms) =>
  Promise.race([
    p,
    sleep(ms).then(() => { throw Error("timeout") })
  ]);
  
const neverResolve = () => new Promise(() => {});

(async function() {
  try {
    console.log("connecting...");
    await timeout(neverResolve(), 2000);
  }
  catch (err) {
    console.error(err);
  }
})();
英文:

neverResolve neither resolves or rejects, so the program hangs indefinitely at the await. Consider abstracting the timeout functionality in its own generic function, timeout -

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

const sleep = ms =&gt;
  new Promise(r =&gt; setTimeout(r, ms));

const timeout = (p, ms) =&gt;
  Promise.race([
    p,
    sleep(ms).then(() =&gt; { throw Error(&quot;timeout&quot;) })
  ]);
  
const neverResolve = () =&gt; new Promise(() =&gt; {});

(async function() {
  try {
    console.log(&quot;connecting...&quot;);
    await timeout(neverResolve(), 2000);
  }
  catch (err) {
    console.error(err);
  }
})();

<!-- end snippet -->

答案2

得分: 1

  1. 你的程序从一个匿名的IIFE异步函数开始执行,因为这是一个异步函数,它立即返回一个Promise给全局范围。所以匿名IIFE的执行被延迟了。你可以通过在你的IIFE调用结束处添加 console.log("OK"); 来轻松验证这一点,它会被打印到控制台。

  2. Node会保持诸如计时器和网络请求的引用计数。当你发起网络请求或其他异步请求,设置一个计时器等时,Node会增加这个引用计数。当计时器/请求完成时,Node会减少计数。参考视频链接

    所以在你的IIFE中发生了以下事情:

    • console.log("START"); <--- 会被打印到控制台
    • await neverResolve(); 这里变得有趣,这个 await 调用会延迟执行并阻塞,直到回调被执行,无论是 resolve 还是 reject

    但在这种情况下,并没有注册任何回调函数,Node.js会认为它已经完成了所有的请求处理,然后终止进程。

英文:

This is what's happening in your case:

  1. Your program starts it's execution from an anonymous IIFE async function, as this is an async function, it immediately returns a Promise to a global scope.So the execution of anonymous IIFE is deferred .
    You can easily validate this by adding a console.log(&quot;OK&quot;); at the end of your IIFE invocation, which is printed to the console

  2. Node keeps a reference count of things like timers and network requests. When you make a network, or other async request, set a timer, etc. Node adds on to this ref count. When the times/request resolve Node subtracts from the count. Ref. video link
    So what happens inside your IIFE is:

    • console.log(&quot;START&quot;); <--- gets printed to console
    • await neverResolve(); Here things get's interesting, this await call will defer the execution and blocks until the callback are executed, either resolve or reject.

    But in this case there are no callbacks registered and nodejs will think that it finished processing all the request and will terminate the process.

答案3

得分: 0

在第一个示例中,`await neverResolve();` 将永远等待并且永远不会解决。在此期间,JavaScript 可以执行其他任务。

在第二个示例中,通过添加`.then()`,你告诉 JavaScript 继续处理下面的代码。通常,下面的所有代码要么在`then()`或`catch()`的回调函数中,这将导致你看到的相同暂停。

这些微妙之处有非常明确的原因,允许你发送一个fetch请求,做其他工作,然后稍后再查看fetch是否完成。请参阅我在标记和稍作修改的示例中的注释。
英文:

In the first example await neverResolve(); waits forever and never resolves as stated. Javascript can go off and do other things in other tasks while waiting for this.

In the second example by adding .then() you've told javascript to continue processing code below. Typically, all the code below would either be inside the callback for then() or catch() which would then create the same pause you're seeing.

There are very deliberate reasons for these nuances that allow you to send a fetch request, do other work and then come back to see if the fetch is done later. See my comments in this marked up and slightly modified example.

async function slowFetch() {
  return new Promise((resolve) =&gt; {
    // a fetch statement that takes forever
    fetch().then(data =&gt; resolve(data));
  });
}

(async () =&gt; {
  try {
    console.log(&quot;START&quot;);

    // start the slow fetch right away
    const dataPromise = slowFetch();

    // do some other tasks that don&#39;t take as long
    await new Promise((resolve) =&gt; setTimeout(() =&gt; resolve(), 5000));

    // wait for the data to arrive
    const data = await dataPromise;

    // do something with the data like fill in the page.

    console.log(&quot;END&quot;);
  } catch (error) {
    console.log(&quot;ERR: &quot;, error);
  }
})();

答案4

得分: 0

执行 neverResolve().then().catch(); 实际上所做的是;

内部的异步 IIFE(立即调用的异步函数表达式)的运行方式与 neverResolve().then().catch(); 相同。你不应该混合使用 Promise 与 async await 抽象。

英文:

By doing neverResolve().then().catch(); what you actually did is;

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

async function neverResolve() {
  return new Promise(() =&gt; {
    console.log(&quot;This promise will never resolve&quot;);
  });
}

(async () =&gt; {
  try {
    console.log(&quot;START&quot;);
    (async function(){
       try {
         await neverResolve()
       }
       catch{
       }
     })(); 
    await new Promise((resolve) =&gt; setTimeout(() =&gt; resolve(), 5000));
    console.log(&quot;END&quot;);
  } catch (error) {
    console.log(&quot;ERR: &quot;, error);
  }
})();

<!-- end snippet -->

The inner async IIFE runs just like neverResolve().then().catch(); does. You should not mix promises with async await abstraction.

答案5

得分: 0

因为您的代码永远无法走到这一步:

await neverResolve();

因为neverResolve()返回一个永远不会解决或拒绝的 promise,这个函数会永远停留在这一行,之后的代码行将不会执行。

await 表示函数的执行应该无限期地暂停,直到您正在等待的 promise 解决或拒绝。


当您将 await 改为以下形式时:

neverResolve().then().catch(); 

这个函数根本没有被暂停。它执行了 neverResolve()。这返回一个 promise,然后在其上调用了 .then()。这个.then() 只是在 promise 上注册了一个回调函数(在 promise 解决时稍后调用)。这返回另一个 promise,然后在其上调用了 .catch(),这只是在 promise 上注册了一个回调函数(在 promise 拒绝时会稍后调用)。

现在,无论如何,您在任何一种情况下都没有传递回调函数,所以这些.then().catch() 实际上没有做任何实际工作,但是即使您向它们传递了回调函数,它们只是注册回调函数并立即返回。.then().catch() 不是阻塞的。它们只是注册一个回调函数并立即返回。因此,在它们返回后,函数中的下一行代码将执行,并且您将得到预期的输出。


总结

  • await 暂停函数执行,直到您正在等待的 promise 解决或拒绝。
  • .then().catch() 只是为某个未来的 promise 状态更改注册回调函数。它们不会阻塞,也不会暂停函数的执行。它们只是注册一个回调函数并立即返回。
英文:

> Why the above function doesn't wait for 5 second and print's the END. It automatically terminates after printing

Because your code never gets past this:

await neverResolve();

Since neverResolve() returns a promise that never resolves or rejects, this function is forever suspended at that line and the lines of code after this statement in the function never execute.

await means that the function execution should be suspended indefinitely until the promise you are awaiting either resolves or rejects.


> But if we execute the same function but with a .then() construct, I get the expected result.

When you change the await to this:

neverResolve().then().catch(); 

The function execute is NOT suspended at all. It executes neverResolve(). That returns a promise which it then calls .then() on. That call to .then() just registers a callback with the promise (to be called later when the promise resolves). That returns another promise which it then calls .catch() on which just registers a callback with the promise (to be called later if/when the promise rejects).

Now, you aren't even passing a callback in either case, so those .then() and .catch() have nothing to actually do, but even if you did pass a callback to each of them, then they would just register that callback and immediately return. .then() and .catch() are not blocking. They just register a callback and immediately return. So, after they return, then next lines of code in the function will execute and you will get the output you were expecting.


Summary

await suspends execution of the function until the promise you are awaiting resolves or rejects.

.then() and .catch() just register callbacks for some future promise state change. They do not block. They do not suspend execution of the function. They register a callback and immediately return.

huangapple
  • 本文由 发表于 2023年2月7日 04:27:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/75366233.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定