关于JavaScript中的Promise的一些问题

时间:2018-12-24 22:57:19

标签: javascript promise

我对Promise在JavaScript中的行为感到困惑,并有一些疑问,有人可以帮我弄清楚吗?

  1. 这是创建Promise的两种常用方法。

    function promise(){
        return new Promise((resolve, reject) => {
            resolve()});
    }
    
    let promise = new Promise(function(resolve, reject){
        resolve();
    });
    

    第一个函数创建Promise对象,但是直到我们调用该函数时才调用它。相比之下,第二条语句创建一个Promise对象并立即调用它。我说的对吗?

  2. 此功能:

    function timeout(ms){
        return new Promise((resolve, reject) => {
            setTimeout(resolve, ms, 'done');
    });
    }
    
    timeout(100).then((value) => {
        console.log(value);
    });
    

    我认为当我们调用超时函数时,它首先创建一个异步函数setTimeout并将其推送到事件队列的末尾。所有同步事件完成后,将调用它并创建Promise对象。 Promise对象也将在其他同步事件之后被推送到事件队列的尾部。所以似乎有两个事件循环,对吗?

  3. 这两个语句相等吗?

    let promise = new Promise(function(resolve, reject){
        resolve();
    });
    
    let promise = Promise.resolve();
    
  4. 为什么输出序列如下所示?

    setTimeout(function(){
        console.log('3');
    }, 0);
    
    Promise.resolve().then(function(){
        console.log('2');
    });
    
    console.log('1');
    
    // 1
    // 2
    // 3
    

    这是本书中的一个示例,作者解释如下:setTimeout(fn, 0)将在下一个事件循环的开始处调用,Promise.resolve()将在当前事件的结束处调用循环,console.log()将立即被调用。

    我对为什么在下一个事件循环开始时调用setTimeout函数感到困惑。我认为它也是一个异步函数,将在当前事件队列的末尾推送,因此应在Promise对象之前调用。有人可以告诉我为什么我错了。

3 个答案:

答案 0 :(得分:2)

  

第一个函数创建Promise对象,但是直到我们调用该函数时才调用它。相比之下,第二条语句创建一个Promise对象并立即调用它。我说的对吗?

第一个功能仅仅是一个功能。只要不调用它,就不会创建任何承诺。调用后,直接执行与您选择的执行没有太大区别。

  

我认为当我们调用超时函数时,它首先创建一个异步函数setTimeout

setTimeout未创建,该函数作为内置函数存在。您可以说它叫

  

...并将其推送到事件队列的末尾。

不是函数setTimeout本身被推入队列。这是回调(在这种情况下为resolve),包括参数,超时时间和唯一计时器ID,该计时器ID置于活动计时器列表中。

  

所有同步事件结束后,将调用它并创建Promise对象。

在调用timeout函数的过程中创建了Promise对象。

传递给new Promise的回调通常称为 promise构造函数回调,正是在执行new Promise的那一刻,回调也得以执行(同步),并创建了承诺。

当同步代码完成时,即当调用堆栈为空时,将首先使用微任务队列。在此阶段,这里什么都没有,因此可以验证任务队列。如果到那时活动计时器已过期,则任务队列中将有一个相应的条目。

  

Promise对象也将在其他同步事件之后被推送到事件队列的尾部

promise对象不会放入任何队列。在计时器到期时,排队事件将作为新任务被触发,即将调用resolve函数,这将反过来解决承诺,这将把条目放入微任务队列中,一个每个then回调和与await相关的效果。在将其他任务从任务队列中拉出之前,将在同一(宏)任务中处理微任务队列。

  

所以似乎有两个事件循环,对吗?

至少两个;可以有更多。例如,在浏览器上下文中,可能还有另一个队列,用于与DOM元素突变有关的事件。 请参见this answer,了解一些不同的规范对此的看法。

  
      
  1. 这两个语句相等吗?
  2.   

实际上是; Promise.resolve()代表new Promise(r => r())

  

为什么输出序列如下所示?

setTimeout包含一个任务队列,而.then包含一个微任务队列,该微任务队列总是在处理任务队列之前被消耗;至少这是当前实施中的共识。

附录

以下是对以下代码的事件序列的一些澄清:

function timeout(ms){
    return new Promise((resolve, reject) => {
        setTimeout(resolve, ms, 'done');
    });
}

timeout(100).then((value) => {
    console.log(value);
});

首先对代码进行解析,并创建一个任务来执行它:

任务1

  • 已定义函数timeout(以该函数为值的提升变量)
  • timeout的参数为100
  • 创建了promise对象,并同步调用了构造函数回调。这两个参数由系统提供。它们是内部函数,供我们在承诺应解决为实现状态或拒绝状态(由我们决定)时调用。
  • setTimeout被调用。
  • 传递给setTimeout的参数用于在内部计时器列表中创建一个条目。
  • 返回了计时器的ID,但您的代码未使用返回值。
  • 创建的Promise对象将返回给timeout的调用者
  • 调用方调用此承诺的then方法,并将其传递给函数(带有console.log的函数)
  • promise内部实现将此功能存储在队列(而不是事件队列)中,以供以后执行。
  • 到达代码末尾,调用堆栈为空
  • 检查微任务队列中是否有任何条目,但它也是空的

在后台:

  • 计时器到期,并且条目被推送到任务队列。这是传递给setTimeout的函数的挂起调用,在本例中为resolve。这种情况的确切发生方式取决于实现。 本质是,在某些时候,任务队列中有可以消耗的任务。

任务2

  • 找到并处理任务队列中的条目(以调用resolve
  • resolve被称为
  • promise内部(实现此resolve函数)将promise标记为已解决
  • promise内部在微任务队列中创建条目:对于存储在其内部队列中的每个then回调(或await),都应创建一个条目。 在这种情况下,只有这样一个条目:匿名函数,该匿名函数传递给了代码中唯一的then方法。
  • 调用栈为空(它只是对resolve的调用)
  • 检查微任务队列中是否有任何条目,并且有一个条目:

任务2,微型任务1

  • 使用微任务队列中的单个条目
  • 匿名函数称为
  • console.log被执行
  • console实现产生输出
  • 调用栈为空
  • 微型任务队列中没有更多条目

任务队列中没有其他条目。系统继续检查任务队列中是否有新条目。

答案 1 :(得分:1)

  1. 不,在您调用函数之前,函数不会创建承诺。
  2. 当您调用timeout(100)时将创建承诺,它将在setTimeout上解析。不确定事件队列的详细信息。
  3. 似乎如此(在控制台中尝试过)。
  4. 一定要在Philip Roberts: What the heck is the event loop anyway? | JSConf EU分钟的11:30左右观看。

答案 2 :(得分:-5)

如果您使用的是新的Promise,则意味着您正在调用一个构造函数,该构造函数将在定义数据类型和将promise定义为函数时首先执行