Почему в 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)")
казнить сразу (или вообще)? - Почему второе обещание не вызвало исключение, когда я не передал функцию?
3 ответа
Код
console.log("before 3 seconds (instantly)")
является выражением, в частности выражением вызова функции. Везде, где это появляется, это означает одно и то же, включая появление в качестве аргумента .then()
Метод Обещания. Как и в любом другом подобном языке, выражение, используемое в вызове функции, вычисляется до вызова функции, поэтому
.then(console.log("before 3 seconds (instantly)"))
результаты в console.log()
сначала вызывается функция, затем возвращаемое значение передается .then()
, Вот почему вы видите сообщение в консоли сразу.
Переходя undefined
в .then()
разрешено, и так как это то, что console.log()
возвращается, ошибки не возникает.
Если ты хочешь это console.log()
чтобы произойти, когда обещание выполнено, вы бы обернули его в функцию:
.then(function() { console.log("after 3 seconds"); })
Почему в Promise.then() можно передать нефункциональный параметр, не вызывая ошибки?
Да. Все не функциональные аргументы должны игнорироваться. Увидеть ниже.
Почему console.log("до 2 секунд (мгновенно)") исполняется сразу (или вообще)?
Потому что в JS аргументы к вызовам функций оцениваются мгновенно (аппликативный порядок).
Почему второе обещание не вызвало исключение, когда я не передал функцию?
Так как console.log
возвращается undefined
а также .then()
без аргументов допустимо (потому что оба обработчика являются необязательными). В вашем примере console.log()
возвращает неопределенное значение, так что это похоже на вызов .then()
без аргументов.
Но даже если он вызывается с некоторыми аргументами, которые не являются функциями, они все равно будут игнорироваться. Например, даже в этом примере "ОК" все равно дойдет до 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 не является функцией, ее следует игнорировать.
Почему
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 в качестве действий по расчету. Результатом является обещание resultCapability.
- Утверждаю: IsPromise(обещание) верно.
- Утверждение: resultCapability является записью PromiseCapability.
- Если IsCallable (onFulfilled) имеет значение false, то
- Пусть на выполненном будет
"Identity"
,- Если IsCallable (onRejected) имеет значение false, то
- Пусть onRejected будет
"Thrower"
,- Пусть executeReaction будет PromiseReaction {[[Capabilities]]: resultCapability, [[Handler]]: onFulfilled }.
- ...
Существенные моменты здесь (3) и (5). В (3), так как onFulfilled является undefined
, IsCallable (onFulfilled) является ложным, и поэтому onFulfilled установлено в "Identity"
, Затем в (5) создается PromiseReaction с [[Handler]] onFulfulled, который, как мы знаем, "Identity"
,
Вот что говорится в разделе " Записи PromiseReaction Records". "Identity"
:
Если [[Handler]]
"Identity"
это эквивалентно функции, которая просто возвращает свой первый аргумент.
Так что у вас есть это. призвание then(undefined)
в основном то же самое, что звонить then(a => a)
Вот почему вы не получите ошибку.