简体   繁体   English

返回新的 Promise 和 Promise.resolve 之间的区别

[英]Difference between returning new Promise and Promise.resolve

For the below code snippet, i would like to understand how NodeJS runtime handles things:对于下面的代码片段,我想了解 NodeJS 运行时如何处理事情:

const billion = 1000000000;

function longRunningTask(){
    let i = 0;
    while (i <= billion) i++;

    console.log(`Billion loops done.`);
}

function longRunningTaskProm(){
    return new Promise((resolve, reject) => {
        let i = 0;
        while (i <= billion) i++;

        resolve(`Billion loops done : with promise.`);
    });
}

function longRunningTaskPromResolve(){
    return Promise.resolve().then(v => {
        let i = 0;
        while (i <= billion) i++;

        return `Billion loops done : with promise.resolve`;
    })
}


console.log(`*** STARTING ***`);

console.log(`1> Long Running Task`);
longRunningTask();

console.log(`2> Long Running Task that returns promise`);
longRunningTaskProm().then(console.log);

console.log(`3> Long Running Task that returns promise.resolve`);
longRunningTaskPromResolve().then(console.log);

console.log(`*** COMPLETED ***`);

1st approach:第一种方法:

longRunningTask() function will block the main thread, as expected. longRunningTask() function 将按预期阻塞主线程。

2nd approach:第二种方法:

In longRunningTaskProm() wrapping the same code in a Promise, was expecting execution will move away from main thread and run as a micro-task.在将相同代码包装在 Promise 中的longRunningTaskProm()中,期望执行将远离主线程并作为微任务运行。 Doesn't seem so, would like to understand what's happening behind the scenes.似乎不是这样,想了解幕后发生的事情。

3rd approach:第三种方法:

Third approach longRunningTaskPromResolve() works.第三种方法longRunningTaskPromResolve()有效。

Here's my understanding:这是我的理解:

Creation and execution of a Promise is still hooked to the main thread. Promise 的创建和执行仍然与主线程挂钩。 Only Promise resolved execution is moved as a micro-task.只有 Promise 解析的执行作为微任务移动。

Am kinda not convinced with whatever resources i found & with my understanding.我有点不相信我发现的任何资源和我的理解。

All three of these options run the code in the main thread and block the event loop.所有这三个选项都在主线程中运行代码并阻止事件循环。 There is a slight difference in timing for WHEN they start running the while loop code and when they block the event loop which will lead to a difference in when they run versus some of your console messages.他们开始运行while循环代码和阻塞事件循环的时间略有不同,这将导致它们运行的时间与您的某些控制台消息的不同。

The first and second options block the event loop immediately.第一个和第二个选项立即阻止事件循环。

The third option blocks the event loop starting on the next tick - that's when Promise.resolve().then() calls the callback you pass to .then() (on the next tick).第三个选项阻止事件循环从下一个滴答开始 - 那是Promise.resolve().then()调用您传递给 .then() 的回调.then()在下一个滴答中)。


The first option is just pure synchronous code.第一个选项只是纯同步代码。 No surprise that it immediately blocks the event loop until the while loop is done.毫不奇怪,它会立即阻塞事件循环,直到while循环完成。

In the second option the new Promise executor callback function is also called synchronously so again it blocks the event loop immediately until the while loop is done.在第二个选项中,新的 Promise 执行器回调 function 也被同步调用,因此它再次立即阻塞事件循环,直到while循环完成。

In the third option, it calls:在第三个选项中,它调用:

Promise.resolve().then(yourCallback);

The Promise.resolve() creates an already resolved promise and then calls .then(yourCallback) on that new promise. Promise.resolve()创建一个已解析的 promise,然后在新的 promise 上调用 .then .then(yourCallback) This schedules yourCallback to run on the next tick of the event loop.这会安排yourCallback在事件循环的下一个滴答声中运行。 Per the promise specification, .then() handlers are always run on a future tick of the event loop, even if the promise is already resolved.根据 promise 规范,即使 promise 已经解决,.then .then()处理程序始终在事件循环的未来滴答声上运行。

Meanwhile, any other Javascript right after this continues to run and only when that Javascript is done does the interpreter get to the next tick of the event loop and run yourCallback .同时,任何其他 Javascript 在此之后继续运行,并且只有当 Javascript 完成时,解释器才会进入事件循环的下一个滴答声并运行yourCallback But, when it does run that callback, it's run in the main thread and therefore blocks until it's done.但是,当它运行该回调时,它会在主线程中运行,因此会阻塞直到它完成。

Creation and execution of a Promise is still hooked to the main thread. Promise 的创建和执行仍然与主线程挂钩。 Only Promise resolved execution is moved as a micro-task.只有 Promise 解析的执行作为微任务移动。

All your code in your example is run in the main thread.您示例中的所有代码都在主线程中运行。 A .then() handler is scheduled to run in a future tick of the event loop (still in the main thread). .then()处理程序计划在事件循环的未来滴答中运行(仍在主线程中)。 This scheduling uses a micro task queue which allows it to get in front of some other things in the event queue, but it still runs in the main thread and it still runs on a future tick of the event loop.这种调度使用了一个微任务队列,它允许它在事件队列中的一些其他事物之前获得,但它仍然在主线程中运行,并且它仍然在事件循环的未来滴答声中运行。

Also, the phrase "execution of a promise" is a bit of a misnomer.此外,“执行承诺”这个短语有点用词不当。 Promises are a notification system and you schedule to run callbacks with them at some point in the future using .then() or .catch() or .finally() on a promise. Promise 是一个通知系统,您计划在未来的某个时间使用 promise 上的 .then( .then().catch().finally()运行回调。 So, in general, you don't want to think of "executing a promise".因此,一般来说,您不想考虑“执行承诺”。 Your code executes causing a promise to get created and then you register callbacks on that promise to run in the future based on what happens with that promise.您的代码执行导致 promise 被创建,然后您在该 promise 上注册回调,以便将来根据 promise 发生的情况运行。 Promises are a specialized event notification system. Promises 是一个专门的事件通知系统。


Promises help notify you when things complete or help you schedule when things run. Promise 有助于在事情完成时通知您,或帮助您安排事情的运行时间。 They don't move tasks to another thread.他们不会将任务移动到另一个线程。


As an illustration, you can insert a setTimeout(fn, 1) right after the third option and see that the timeout is blocked from running until the third option finishes.作为说明,您可以在第三个选项之后插入一个setTimeout(fn, 1)并看到超时被阻止运行,直到第三个选项完成。 Here's an example of that.这是一个例子。 And, I've made the blocking loops all be 1000ms long so you can more easily see.而且,我已将阻塞循环全部设为 1000 毫秒长,以便您更容易看到。 Run this in the browser here or copy into a node.js file and run it there to see how the setTimeout() is blocked from executing on time by the execution time of longRunningTaskPromResolve() .在此处的浏览器中运行它或复制到 node.js 文件并在那里运行它以查看setTimeout()是如何被longRunningTaskPromResolve()的执行时间阻止按时执行的。 So, longRunningTaskPromResolve() is still blocking.所以, longRunningTaskPromResolve()仍然阻塞。 Putting it inside a .then() handler changes when it gets to run, but it is still blocking.将它放在.then()处理程序中会在它开始运行时发生变化,但它仍然是阻塞的。

 const loopTime = 1000; let startTime; function log(...args) { if (.startTime) { startTime = Date;now(). } let delta = (Date;now() - startTime) / 1000. args.unshift(delta:toFixed(3) + ";"). console.log(..;args); } function longRunningTask(){ log('longRunningTask() starting'). let start = Date;now(). while (Date;now() - start < loopTime) {} log('** longRunningTask() done **'); } function longRunningTaskProm(){ log('longRunningTaskProm() starting'), return new Promise((resolve. reject) => { let start = Date;now(). while (Date;now() - start < loopTime) {} log('About to call resolve() in longRunningTaskProm()'). resolve('** longRunningTaskProm();then(handler) called **'); }); } function longRunningTaskPromResolve(){ log('longRunningTaskPromResolve() starting'). return Promise.resolve().then(v => { log('Start running;then() handler in longRunningTaskPromResolve()'). let start = Date;now(). while (Date.now() - start < loopTime) {} log('About to return from;then() in longRunningTaskPromResolve()'). return '** longRunningTaskPromResolve();then(handler) called **'; }) } log('*** STARTING ***'); longRunningTask(). longRunningTaskProm();then(log). longRunningTaskPromResolve();then(log); log('Scheduling 1ms setTimeout') setTimeout(() => { log('1ms setTimeout Got to Run'), }; 1), log('*** First sequence of code completed; returning to event loop ***');

If you run this snippet and look at exactly when each message is output and the timing associated with each message, you can see the exact sequence of when things get to run.如果您运行此代码段并查看每条消息的确切时间是 output 以及与每条消息关联的时间,您可以看到事情开始运行的确切顺序。

Here's the output when I run it in node.js (line numbers added to help with the explanation below):这是我在 node.js 中运行时的 output (添加行号以帮助以下解释):

1    0.000: *** STARTING ***
2    0.005: longRunningTask() starting
3    1.006: ** longRunningTask() done **
4    1.006: longRunningTaskProm() starting
5    2.007: About to call resolve() in longRunningTaskProm()
6    2.007: longRunningTaskPromResolve() starting
7    2.008: Scheduling 1ms setTimeout
8    2.009: *** First sequence of code completed, returning to event loop ***
9    2.010: ** longRunningTaskProm().then(handler) called **
10   2.010: Start running .then() handler in longRunningTaskPromResolve()
11   3.010: About to return from .then() in longRunningTaskPromResolve()
12   3.010: ** longRunningTaskPromResolve().then(handler) called **
13   3.012: 1ms setTimeout Got to Run

Here's a step-by-step annotation:这是一步一步的注释:

  1. Things start.事情开始了。
  2. longRunningTask() initiated. longRunningTask()启动。
  3. longRunningTask() completes. longRunningTask()完成。 It is entirely synchronous.它是完全同步的。
  4. longRunningTaskProm() initiated. longRunningTaskProm()启动。
  5. longRunningTaskProm() calls resolve() . longRunningTaskProm()调用resolve() You can see from this that the promise executor function (the callback passed to new Promise(fn)` is entirely synchronous too.从中可以看出 promise 执行器 function (传递给 new Promise(fn) 的回调也是完全同步的。
  6. longRunningTaskPromResolve() initiated. longRunningTaskPromResolve()启动。 You can see that the handler from longRunningTaskProm().then(handler) has not yet been called.您可以看到longRunningTaskProm().then(handler)中的处理程序尚未被调用。 That has been scheduled to run on the next tick of the event loop, but since we haven't gotten back to the event loop yet, it hasn't yet been called.这已被安排在事件循环的下一个滴答声中运行,但由于我们还没有回到事件循环,它还没有被调用。
  7. We're now setting the 1ms timer.我们现在正在设置 1ms 计时器。 Note that this timer is being set only 1ms after we started longRunningTaskPromResolve() .请注意,在我们启动longRunningTaskPromResolve()后仅 1ms 设置了此计时器。 That's because longRunningTaskPromResolve() didn't do much yet.那是因为longRunningTaskPromResolve()还没有做太多事情。 It ran Promise.resolve().then(handler) , but all that did was schedule the handler to run on a future tick of the event loop.它运行Promise.resolve().then(handler) ,但所做的只是安排handler在事件循环的未来滴答声中运行。 So, that only took 1ms to schedule that.所以,只需要 1 毫秒来安排它。 The long running part of that function hasn't started running yet. function 的长期运行部分尚未开始运行。
  8. We get to the end of this sequence of code and return back to the event loop.我们到此代码序列的末尾并返回到事件循环。
  9. The next thing scheduled to run in the event loop is the handler from longRunningTaskProm().then(handler) so that gets called.计划在事件循环中运行的下一件事是来自longRunningTaskProm().then(handler)的处理程序,以便被调用。 You can see that it was already waiting to run since it ran only 1ms after we returned to the event loop.您可以看到它已经在等待运行,因为它在我们返回事件循环后仅运行了 1 毫秒。 That handler runs and we return back to the event loop.该处理程序运行,我们返回到事件循环。
  10. The next thing scheduled to run in the event loop is the handler from Promise.resolve().then(handler) so we now see that that starts to run and since it was already queued, it runs immediately after the previous event finished.计划在事件循环中运行的下一件事是来自Promise.resolve().then(handler)的处理程序,所以我们现在看到它开始运行,并且由于它已经排队,它在前一个事件完成后立即运行。
  11. It takes exactly 1000ms for the loop in longRunningTaskPromResolve() to run and then it returns from it's .then() handler which schedules then next .then() handler in that promise chain to run on the next tick of the eventl loop. longRunningTaskPromResolve()中的循环运行恰好需要 1000 毫秒,然后它从它的.then()处理程序返回,该处理程序安排该 promise 链中的下一个 .then .then()处理程序在 eventl 循环的下一个滴答时运行。
  12. That .then() gets to run..then()开始运行。
  13. Then, finally when there are no .then() handlers scheduled to run, the setTimeout() callback gets to run.然后,最后当没有.then()处理程序计划运行时, setTimeout()回调开始运行。 It was set to run in 1ms, but it got delayed by all the promise action running at a higher priority ahead of it so instead of running 1ms, it ran in 1004ms.它被设置为在 1 毫秒内运行,但它被所有 promise 操作以更高的优先级运行而延迟,因此它不是运行 1 毫秒,而是在 1004 毫秒内运行。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM