Почему в 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)
, и это не исключение. Давайте посмотрим, что он говорит:
Абстрактная операция 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)
, поэтому вы не получаете ошибку.