¿Por qué es posible pasar un parámetro no funcional a Promise.then () sin causar un error?

Tengo los siguientes

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)")); 

que produce el siguiente resultado:

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

Promise.then () espera una función onFulfilled , pero la pasé en console.log("before 2 seconds (instantly)") , que no es una función. Pregunta en dos partes:

  • ¿Por qué console.log("before 2 seconds (instantly)") se ejecuta de inmediato (o en absoluto)?
  • ¿Por qué la segunda Promesa no generó una excepción cuando no pasé una función?

El código

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

es una expresión , específicamente una expresión llamada de función. Dondequiera que aparezca, significa lo mismo, incluida una apariencia como argumento del método .then() de una promesa. Como en cualquier otro lenguaje similar, una expresión utilizada en una llamada de función se evalúa antes de la llamada de función, por lo que

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

da como resultado que la función console.log() se llama primero , y el valor de retorno se pasa a .then() . Es por eso que ves el mensaje en la consola inmediatamente.

Se permite pasar undefined a .then() , y como eso es lo que console.log() devuelve, no se produce ningún error.

Si desea que console.log() suceda cuando se cumpla la Promesa, lo envolverá en una función:

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

¿Por qué es posible pasar un parámetro no funcional a Promise.then () sin causar un error?

Sí. Todos los argumentos que no son de función deben ser ignorados. Vea abajo.

¿Por qué console.log (“antes de 2 segundos (instantáneamente)”) se ejecuta de inmediato (o en absoluto)?

Porque en JS los argumentos a funciones llamadas son evaluados instantáneamente (orden aplicativo).

¿Por qué la segunda Promesa no generó una excepción cuando no pasé una función?

Porque console.log devuelve undefined y .then() sin argumentos es legal (porque ambos controladores son opcionales). En su ejemplo, console.log() devuelve undefined por lo que es como llamar a .then() sin argumentos.

Pero incluso si se llamara con algunos argumentos que no son funciones, se ignorarían. Por ejemplo, incluso en este ejemplo, el ‘ok’ todavía llegaría al console.log al final, lo que puede ser sorprendente:

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

Consulte la especificación Promesas / A + , sección 2.2.1, que describe los argumentos del método .then() :

2.2.1 Tanto onFulfilled como onRejected son argumentos opcionales:

  • Si onFulfilled no es una función, debe ignorarse.
  • Si onRejected no es una función, debe ignorarse.

¿Por qué console.log("before 2 seconds (instantly)") se ejecuta de inmediato (o en absoluto)?

Los parámetros de una función se evalúan antes de llamar a la función. Cuando haces alert(1+2) , esperas que 1+2 se evalúe 1+2 , y cuando haces alert(console.log("...")) también debes esperar console.log("...") para ser evaluado primero. No hay nada especial acerca de then ; es solo una función regular y sus argumentos se tratan igual que los argumentos de cualquier otra función.

¿Por qué la segunda Promesa no generó una excepción cuando no pasé una función?

Debido a que console.log devuelve undefined , y la especificación de idioma ( ECMAScript 2015 ) dice lo que debería suceder cuando llamas a then(undefined) , y no está lanzando una excepción. Veamos lo que dice:

25.4.5.3.1 PerformPromiseThen (promesa, onFulfilled, onRejected, resultCapability)

La operación abstracta PerformPromiseThen realiza la operación “entonces” en promesa utilizando onFulfilled y onRejected como sus acciones de liquidación. El resultado es la promesa de resultCapability .

  1. Afirmar: IsPromise ( promesa ) es cierto .
  2. Assert: resultCapability es un registro de PromiseCapability.
  3. Si IsCallable ( onFulfilled ) es falso , entonces
    1. Deje que se cumpla la "Identity" .
  4. Si IsCallable ( onRejected ) es falso , entonces
    1. Deje que OnRejected sea "Thrower" .
  5. Deje que se cumplaReaction el PromiseReaction {[[Capacidades]]: resultCapability , [[Handler]]: onFulfilled }.

Los puntos más destacados aquí son (3) y (5). En (3), dado que onFulfilled undefined está undefined , IsCallable ( onFulfilled ) es falso y, por lo tanto, onFulfilled se establece en "Identity" . Luego, en (5), se crea un PromiseReaction con el [[Handler]] onFulfulled , que sabemos que es "Identity" .

Esto es lo que dice la sección Registros de PromiseReaction sobre "Identity" :

Si [[Handler]] es "Identity" es equivalente a una función que simplemente devuelve su primer argumento.

Así que ahí lo tienen. Llamar then(undefined) es básicamente lo mismo que llamar then(a => a) , razón por la cual no recibe un error.