返回新的Promise和Promise.resolve之间的区别 [英] Difference between returning new Promise and Promise.resolve

查看:202
本文介绍了返回新的Promise和Promise.resolve之间的区别的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于以下代码段,我想了解NodeJS运行时如何处理事情:

For the below code snippet, i would like to understand how NodeJS runtime handles things :

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 ***`);

第一种方法:

longRunningTask()函数将按预期阻塞主线程.

longRunningTask() function will block the main thread, as expected.

第二种方法:

longRunningTaskProm()中,将相同的代码包装在Promise中,期望执行将从主线程移开并作为微任务运行.似乎并非如此,想了解幕后发生的事情.

In longRunningTaskProm() wrapping the same code in a Promise, was expecting execution will move away from main thread and run as a micro-task. Doesn't seem so, would like to understand what's happening behind the scenes.

第三种方法:

第三种方法 longRunningTaskPromResolve()有效.

这是我的理解:

Promise的创建和执行仍然与主线程挂钩.只有Promise解决的执行作为微任务移动.

Creation and execution of a Promise is still hooked to the main thread. Only Promise resolved execution is moved as a micro-task.

我有点不相信我发现的任何资源&以我的理解.

Am kinda not convinced with whatever resources i found & with my understanding.

推荐答案

这三个选项都在主线程中运行代码并阻止事件循环.他们开始运行while循环代码和何时阻止事件循环的时间稍有不同,这将导致它们与某些控制台消息的运行时间有所不同.

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.

第一个和第二个选项会立即阻止事件循环.

The first and second options block the event loop immediately.

第三个选项阻止从下一个刻度开始的事件循环-即Promise.resolve().then()调用您传递给.then()的回调(在下一个刻度).

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).

第一个选项只是纯同步代码.毫不奇怪,它会立即阻塞事件循环,直到完成while循环.

The first option is just pure synchronous code. No surprise that it immediately blocks the event loop until the while loop is done.

在第二个选项中,新的Promise执行程序回调函数也被同步调用,因此它再次阻塞事件循环,直到完成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.

在第三个选项中,它调用:

In the third option, it calls:

Promise.resolve().then(yourCallback);

Promise.resolve()创建一个已经解决的承诺,然后对该新承诺调用.then(yourCallback).这样可以安排yourCallback在事件循环的下一个计时周期运行.根据promise规范,即使已解决promise,.then()处理程序也始终在事件循环的将来时刻运行.

The Promise.resolve() creates an already resolved promise and then calls .then(yourCallback) on that new promise. This schedules yourCallback to run on the next tick of the event loop. Per the promise specification, .then() handlers are always run on a future tick of the event loop, even if the promise is already resolved.

同时,紧随其后的任何其他Java脚本将继续运行,并且只有在完成该Java脚本后,解释器才能到达事件循环的下一个刻度,并运行yourCallback.但是,当它确实运行该回调时,它会在主线程中运行,因此会阻塞直到完成.

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. But, when it does run that callback, it's run in the main thread and therefore blocks until it's done.

Promise的创建和执行仍然与主线程挂钩.只有Promise解决的执行作为微任务移动.

Creation and execution of a Promise is still hooked to the main thread. Only Promise resolved execution is moved as a micro-task.

示例中的所有代码都在主线程中运行.计划.then()处理程序在事件循环的将来时间中运行(仍在主线程中).这种调度使用了一个微型任务队列,它可以使它排在事件队列中的其他事件之前,但它仍在主线程中运行,并且仍在事件循环的将来时间运行.

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). 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.

此外,履行诺言"这个短语有点用词不当. Promise是一个通知系统,您可以安排在将来某个时间使用.then().catch().finally()在它们上运行回调.因此,总的来说,您不想考虑履行承诺".您的代码将执行,从而导致创建承诺,然后根据该承诺发生的情况注册该承诺的回调,以在将来运行.承诺是一个专门的事件通知系统.

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. 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. Promises are a specialized event notification system.

承诺会在事情完成时通知您,或者帮助您安排事情运行的时间.他们不会将任务移到另一个线程.

Promises help notify you when things complete or help you schedule when things run. They don't move tasks to another thread.

作为说明,您可以在第三个选项之后插入一个setTimeout(fn, 1),并看到在第三个选项完成之前,超时一直被阻止.这是一个例子.而且,我将阻塞循环的长度都设置为1000ms,这样您就可以更轻松地看到.在此处的浏览器中运行此命令,或将其复制到node.js文件中并在其中运行,以查看在longRunningTaskPromResolve()的执行时间之前如何阻止setTimeout()准时执行.因此,longRunningTaskPromResolve()仍处于阻止状态.将其放到.then()处理程序中时,它会开始运行,但仍处于阻塞状态.

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. Here's an example of that. And, I've made the blocking loops all be 1000ms long so you can more easily see. 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(). So, longRunningTaskPromResolve() is still blocking. Putting it inside a .then() handler changes when it gets to run, but it is still blocking.

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.

这是我在node.js中运行时的输出(添加了行号以帮助进行以下说明):

Here's the output when I run it in node.js (line numbers added to help with the explanation below):

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

下面是逐步注释:

  1. 事情开始了.
  2. longRunningTask()已启动.
  3. longRunningTask()完成.这完全是同步的.
  4. longRunningTaskProm()已启动.
  5. longRunningTaskProm()调用resolve().从中可以看出,promise executor函数(传递给new Promise(fn)`的回调也是完全同步的.
  6. longRunningTaskPromResolve()已启动.您会看到尚未调用longRunningTaskProm().then(handler)中的处理程序.已经计划在事件循环的下一个时刻运行该循环,但是由于我们还没有回到事件循环,因此尚未调用它.
  7. 我们现在正在设置1ms计时器.请注意,在启动longRunningTaskPromResolve()之后仅1毫秒就设置了此计时器.这是因为longRunningTaskPromResolve()并没有做太多事情.它运行了Promise.resolve().then(handler),但所做的只是安排handler在事件循环的将来时间运行.因此,仅需1毫秒即可安排该时间.该功能的长期运行部分尚未开始运行.
  8. 我们到达此代码序列的结尾,然后返回事件循环.
  9. 计划在事件循环中运行的下一件事情是longRunningTaskProm().then(handler)中的处理程序,因此将被调用.您可以看到它已经在等待运行,因为它在我们返回事件循环后仅运行了1毫秒.该处理程序将运行,然后我们返回事件循环.
  10. 计划在事件循环中运行的下一个事件是Promise.resolve().then(handler)中的处理程序,因此我们现在看到该事件开始运行,并且由于已经排队,因此它在上一个事件完成后立即运行.
  11. longRunningTaskPromResolve()中的循环运行完全需要1000毫秒,然后从它的.then()处理程序返回,该处理程序调度该诺言链中的下一个.then()处理程序,使其在eventl循环的下一个计时周期运行./li>
  12. .then()开始运行.
  13. 然后,最后,当没有计划运行的.then()处理程序时,setTimeout()回调将运行.它原定要在1ms内运行,但是由于所有的promise操作都以更高的优先级运行而延迟了,所以它没有运行1ms,而是运行了1004ms.
  1. Things start.
  2. longRunningTask() initiated.
  3. longRunningTask() completes. It is entirely synchronous.
  4. longRunningTaskProm() initiated.
  5. longRunningTaskProm() calls resolve(). You can see from this that the promise executor function (the callback passed to new Promise(fn)` is entirely synchronous too.
  6. longRunningTaskPromResolve() initiated. You can see that the handler from longRunningTaskProm().then(handler) has not yet been called. 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. Note that this timer is being set only 1ms after we started longRunningTaskPromResolve(). That's because longRunningTaskPromResolve() didn't do much yet. It ran Promise.resolve().then(handler), but all that did was schedule the handler to run on a future tick of the event loop. So, that only took 1ms to schedule that. The long running part of that function hasn't started running yet.
  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. You can see that it was already waiting to run since it ran only 1ms after we returned to the event loop. 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.
  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.
  12. That .then() gets to run.
  13. Then, finally when there are no .then() handlers scheduled to run, the setTimeout() callback gets to run. 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.

这篇关于返回新的Promise和Promise.resolve之间的区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆