Почему в Promise.then() можно передать нефункциональный параметр без возникновения ошибки?

У меня есть следующее:

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)") выполняется сразу (или вообще)?
  • Почему второе обещание не вызвало исключение, когда я не прошел функцию?

Ответ 1

Код

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

- выражение , в частности выражение функции. Везде, где это появляется, это означает одно и то же, включая появление в качестве аргумента метода .then() обещания. Как и на любом другом подобном языке, выражение, используемое в вызове функции, вычисляется перед вызовом функции, поэтому

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

приводит к тому, что функция console.log() называется first, а затем возвращается значение .then(). Вот почему вы сразу видите сообщение в консоли.

Допускается undefined до .then(), и с тех пор, как возвращается console.log(), ошибка не возникает.

Если вы хотите, чтобы console.log() выполнялся, когда обещание выполнено, вы завернете его в функцию:

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

Ответ 2

Почему возможно передать не функциональный параметр в Promise.then() без возникновения ошибки?

Да. Все нефункциональные аргументы следует игнорировать. См. Ниже.

Почему console.log( "до 2 секунд (мгновенно)" ) выполняется сразу (или вообще)?

Поскольку в JS аргументы вызовам функций вычисляются мгновенно (аппликативный порядок).

Почему второе обещание не вызвало исключение, когда я не прошел функцию?

Потому что 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 2 seconds (instantly)") выполняется сразу же (или вообще)?

Параметры функции оцениваются до вызова функции. Когда вы выполняете alert(1+2), вы ожидаете, что 1+2 будет оценен первым, а когда вы сделаете alert(console.log("...")), вы также должны ожидать, что console.log("...") будет оцениваться первым. Нет ничего особенного в then; это просто регулярная функция, и ее аргументы обрабатываются так же, как и любые другие аргументы функции.

Почему второе обещание не вызвало исключение, когда я не прошел функцию?

Поскольку console.log возвращает undefined, а спецификация языка (ECMAScript 2015) говорит, что должно произойти, когда вы вызываете then(undefined), и это не исключение. Давайте посмотрим, что он говорит:

25.4.5.3.1 PerformPromiseThen (обещание, onFulfilled, onRejected, resultCapability)

Абстрактная операция PerformPromiseThen выполняет "then" операции по обещанию с использованием onFulfilled и onRejected as its расчетные действия. Результатом является обещание resultCapabilitys.

  • Утверждение: IsPromise (обещание) true.
  • Assert: resultCapability - это запись PromiseCapability.
  • Если IsCallable (onFulfilled) false, тогда
    • Пусть onFulfilled будет "Identity".
  • Если IsCallable (onRejected) false, тогда
    • Пусть onRejected будет "Thrower".
  • Пусть performReaction является PromiseReaction {[[Capabilities]]: resultCapability, [[Handler]]: onFulfilled}.
  • ...

Здесь важными являются (3) и (5). В (3), так как onFulfilled имеет значение undefined, IsCallable (onFulfilled) false, и поэтому onFulfilled установлен на "Identity". Затем в (5) PromiseReaction создается с помощью [[Handler]] onFulfulled, который, как мы знаем, "Identity".

Здесь раздел Записи PromiseReaction говорит о "Identity":

Если [[Обработчик]] "Identity", он эквивалентен функции, которая просто возвращает свой первый аргумент.

Итак, у вас это есть. Вызов then(undefined) в основном совпадает с вызовом then(a => a), поэтому вы не получаете ошибку.