【问题标题】:Why is it possible to pass in a non-function parameter to Promise.then() without causing an error?为什么可以将非函数参数传递给 Promise.then() 而不会导致错误?
【发布时间】:2017-06-24 23:41:44
【问题描述】:

我有以下几点:

new Promise(resolve => setTimeout(resolve, 2000))
    .then(() => console.log("after 2 seconds"));

new Promise(resolve => setTimeout(resolve, 3000))
    .then(console.log("before 3 seconds (instantly)"));

产生以下输出:

> node index.js
before 3 seconds (instantly)
after 2 seconds

Promise.then() 需要一个onFulfilled 函数,但我传入了console.log("before 2 seconds (instantly)"),它不是一个函数。两部分问题:

  • 为什么console.log("before 2 seconds (instantly)") 会立即(或根本)被执行?
  • 为什么我没有传入函数时第二个 Promise 没有引发异常?

【问题讨论】:

  • "为什么 console.log("before 2 seconds (instantly)") 会立即(或根本)执行?为什么不会立即执行?在将表达式的结果作为参数传递之前,需要对其进行评估。在这种情况下,评估console.log("before 3 seconds (instantly)") 会导致将内容记录到控制台,然后将undefined 结果用作调用Promise#then 的参数。
  • my answer below 回答您的问题了吗?有没有cmets?

标签: javascript node.js promise


【解决方案1】:

为什么console.log("before 2 seconds (instantly)") 会立即(或根本)被执行?

函数的参数在函数被调用之前被评估。当您执行alert(1+2) 时,您期望首先评估1+2,而当您执行alert(console.log("...")) 时,您同样应该期望首先评估console.log("...")。 then 没有什么特别之处;它只是一个常规函数,它的参数被视为与任何其他函数的参数相同。

为什么我没有传入函数时第二个 Promise 没有引发异常?

因为console.log 返回undefined,并且语言规范 (ECMAScript 2015) 说明了当您调用 then(undefined) 时应该发生什么,并且它不会引发异常。让我们看看它是怎么说的:

25.4.5.3.1 PerformPromiseThen ( promise, onFulfilled, onRejected, resultCapability )

抽象操作 PerformPromiseThen 执行“then” 使用 onFulfilled 和 onRejected 作为对 promise 的操作 和解行动。结果是 resultCapability 的承诺。

  1. 断言:IsPromise(promise) 为 true。
  2. 断言:resultCapability 是 PromiseCapability 记录。
  3. 如果 IsCallable(onFulfilled) 为 false,则
    1. 让 onFulfilled 为 "Identity"。
  4. 如果 IsCallable(onRejected) 为 false,则
    1. 让 onRejected 为 "Thrower"。
  5. 让 fulfillReaction 成为 PromiseReaction { [[Capabilities]]: resultCapability, [[Handler]]: onFulfilled }。
  6. ...

这里的重点是(3)和(5)。在 (3) 中,由于 onFulfilled 是 undefined,IsCallable(onFulfilled) 是 false,所以 onFulfilled 是设置为 "Identity"。然后,在 (5) 中,使用 [[Handler]] onFulfulled 创建一个 PromiseReaction,我们知道它是 "Identity"。

下面是PromiseReaction Records 部分对"Identity" 所说的话:

如果 [[Handler]] 是 "Identity",则它等效于仅返回其第一个参数的函数。

所以你有它。调用then(undefined) 与调用then(a => a) 基本相同,这就是您不会收到错误消息的原因。

【讨论】:

    【解决方案2】:

    为什么可以将非函数参数传递给 Promise.then() 而不会导致错误?

    是的。所有非函数参数都应该被忽略。见下文。

    为什么 console.log("before 2 seconds (instantly)") 会立即(或根本)被执行?

    因为在 JS 中,函数调用的参数是立即评估的(应用顺序)。

    为什么我没有传入函数时第二个 Promise 没有引发异常?

    因为console.log 返回不带参数的undefined 和.then() 是合法的(因为这两个处理程序都是可选的)。在您的示例中,console.log() 返回 undefined,因此就像在没有参数的情况下调用 .then()。

    但是,即使使用一些不是函数的参数调用它,它们仍然会被忽略。例如,即使在这个例子中,“ok”仍然会在最后到达console.log,这可能会令人惊讶:

    Promise.resolve('ok')
        .then()
        .then(false)
        .then(null)
        .then(1)
        .then('x')
        .then([1, 2, 3])
        .then({a: 1, b: 2})
        .then(console.log);
    

    请参阅 Promises/A+ 规范的第 2.2.1 节,其中描述了 .then() 方法的参数:

    2.2.1 onFulfilled 和 onRejected 都是可选参数:

    • 如果 onFulfilled 不是函数,则必须忽略它。
    • 如果 onRejected 不是函数,则必须将其忽略。

    【讨论】:

      【解决方案3】:

      代码

      console.log("before 3 seconds (instantly)")
      

      是一个表达式,特别是一个函数调用表达式。无论出现在哪里,它的含义都是一样的,包括作为 Promise 的 .then() 方法的参数的外观。与任何其他类似语言一样,函数调用中使用的表达式在函数调用之前计算,所以

      .then(console.log("before 3 seconds (instantly)"))
      

      导致console.log() 函数首先被调用,然后将返回值传递给.then()。这就是为什么您会立即在控制台中看到该消息。

      允许将undefined 传递给.then(),因为这是console.log() 返回的内容,所以不会引发错误。

      如果您希望 console.log() 在 Promise 实现时发生,您可以将其包装在一个函数中:

      .then(function() { console.log("after 3 seconds"); })
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-01-10
        • 2019-01-27
        • 1970-01-01
        • 2023-01-28
        • 1970-01-01
        相关资源
        最近更新 更多