sincronas - promises javascript w3schools
¿Por qué el constructor de Promise requiere una función que llame a ''resolver'' cuando esté completo, pero ''entonces'' no lo hace-devuelve un valor en su lugar? (4)
Al sumergirme en el estudio de Promise
s, mi comprensión se ha detenido en la siguiente pregunta que no encuentro discutida (todo lo que encuentro son discusiones específicas sobre el constructor de Promise
y la función Promise
'' then
'', pero no una discusión que compara su diseño patrones).
1. El constructor de la Promise
De la documentación de MDN , tenemos este uso del constructor Promise (con mi comentario agregado):
new Promise(function(resolve, reject) { ... }); // <-- Call this Stage 1
Objeto de función con dos argumentos
resolve
yreject
. El primer argumento cumple la promesa, el segundo argumento la rechaza. Podemos llamar a estas funciones, una vez completada nuestra operación.
2. La función de then
Pasando a la función then
que se puede llamar en un objeto Promise
(que devuelve un nuevo objeto Promise
), tenemos la siguiente firma de función como se describe en la documentación (con mis comentarios agregados):
p.then(onFulfilled, onRejected);
Encadenamiento
Debido a que el método de
then
devuelve una Promesa, puede encadenar fácilmente las llamadas.
var p2 = new Promise(function(resolve, reject) {
resolve(1); // <-- Stage 1 again
});
p2.then(function(value) {
console.log(value); // 1
return value + 1; // <-- Call this Stage 2
}).then(function(value) {
console.log(value); // 2
});
Mi pregunta
Desde el fragmento de código anterior, me parece claro que el valor pasado a la función de resolve
en la Etapa 1 (en la segunda aparición de la resolve
- debajo de (2), arriba) se pasa a la siguiente etapa (la primera then
función que sigue en el mismo fragmento de código). No hay un valor de retorno en la Etapa 1. Sin embargo, es el valor de retorno en la Etapa 2 que se pasa a la siguiente etapa después de eso (la segunda función then
).
Es esta falta de correspondencia entre el patrón de diseño para la creación de una Promise
y el uso de la función then
en una promesa existente (que también devuelve una Promise
), solo una casualidad histórica (uno requiere llamar a una devolución de llamada pero no devuelve nada, y el otro devuelve un valor pero no llama a una devolución de llamada)?
¿O me estoy perdiendo una razón subyacente por la cual el constructor Promise
utiliza un patrón de diseño diferente al de la función then
?
El objetivo principal de la función de ejecutor de constructor de promesa es difundir las funciones de resolución y rechazo al código que no utiliza la promesa, envolverlo y convertirlo en una promesa. Si quisiera limitar esto solo a las funciones síncronas, entonces sí, se podría haber usado un valor de retorno de la función, pero eso hubiera sido una tontería ya que la parte útil es difundir las funciones de resolución y rechazo al código que se ejecuta más tarde. (mucho después de la devolución), por ejemplo, a devoluciones de llamada pasadas a alguna API asíncrona.
Inspirado en las respuestas anteriores (abordaré la parte que me resultó más confusa):
Los argumentos de resolve
y reject
en el constructor Promise no son funciones definidas por usted. Piensa en ellos como enganches que puedes incrustar en tu código de operación asíncrona (por lo general, se resolve
con una respuesta exitosa y se reject
con un fallo), de modo que javascript tenga una manera de marcar la Promesa como Cumplida o Rechazada, dependiendo del resultado de operación asíncrona; una vez que eso sucede, la función apropiada que then(fun1, fun2)
en then(fun1, fun2)
se activa para consumir la Promesa (ya sea fun1(success_response)
o fun2(failure_reason)
, dependiendo de si la Promesa se cumple / rechaza). Dado que fun1
y fun2
son simples funciones de javascript (simplemente toman el resultado futuro de su operación asíncrona como argumentos), return
valores (que pueden undefined
estar undefined
si no lo hacen explícitamente).
También vea grandes artículos de Mozilla:
developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/…
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
No hay correspondencia entre el constructor de Promise
y el método de then
porque son dos cosas independientes, diseñadas para diferentes propósitos.
El constructor Promise
solo se utiliza para promisifying 1 funciones asíncronas. De hecho, como usted dice, se basa en invocar devoluciones de llamadas de resolve
/ reject
para enviar valores de forma asíncrona , y no hay valores de retorno en ese caso.
El hecho de que el propio constructor Promise
tome esta devolución de llamada de "resolución" (a la que pasa de forma sincrónica la resolve
y el reject
) es, de hecho, una mejora del patrón diferido más antiguo, y no tiene ninguna similitud con las devoluciones de llamada de then
.
var p = new Promise(function(res, rej) { | var def = Promise.Deferred();
setTimeout(res, 100); | setTimeout(def.resolve, 100);
}); | var p = def.promise;
Las devoluciones de llamada en contraste son devoluciones de llamada asíncronas clásicas, con la característica adicional que puede return
de ellas. Se invocan de forma asíncrona para recibir valores.
p.then(function(val) { … });
Para resumir las diferencias:
-
Promise
es un constructor, mientras quethen
es un método. -
Promise
toma una devolución de llamada, mientras quethen
toma hasta dos -
Promise
invoca su devolución de llamada de forma síncrona, mientras quethen
invoca su devolución de llamada de forma asíncrona -
Promise
siempre invoca su devolución de llamada,
then
podría no invocar sus devoluciones de llamada (si la promesa no se cumple / rechaza) -
Promise
pasa las capacidades para resolver / rechazar una promesa de devolución de llamada,
then
pasa la razón de valor / rechazo del resultado de la promesa a la que se solicitó -
Promise
invoca su devolución de llamada con el fin de ejecutar efectos secundarios (reject
/resolve
llamada),
then
invoca sus devoluciones de llamada para sus valores de resultado (para el encadenamiento)
Sí, ambos devuelven promesas, aunque comparten ese rasgo con muchas otras funciones ( Promise.resolve
, Promise.reject
, fetch
, ...). De hecho, todos estos se basan en la misma construcción de promesa y capacidades de resolución / rechazo que también proporciona el constructor de Promise
, aunque ese no es su propósito principal. then
básicamente, ofrece la posibilidad de adjuntar devoluciones de llamada onFulfilled
/ onRejected
a una promesa existente, que es más bien diametral al constructor de la Promise
.
El hecho de que ambos utilicen devoluciones de llamada es solo una coincidencia, no una casualidad histórica, sino una coadaptación de una característica del lenguaje.
1: Idealmente, nunca necesitaría esto porque todas las API asíncronas nativas devuelven promesas
La respuesta de Bergi es excelente, y me ha sido de gran ayuda. Esta respuesta es complementaria a la suya. Para visualizar la relación entre el constructor Promise()
y el método then()
, creé este diagrama. Espero que ayude a alguien ... tal vez incluso a mí, dentro de unos meses.
La idea principal aquí es que la función "ejecutor" que se pasa al constructor Promise()
pone en marcha las tareas que establecerán el estado de la promesa; mientras que los manejadores a los que le pasas then()
reaccionarán al estado de la promesa.
(Ejemplos de código adaptados del tutorial clásico de Jake Archibald .)
Esta es una vista muy simplificada de cómo funcionan las cosas, dejando de lado muchos detalles importantes. Pero creo que si uno puede mantener una buena visión general del propósito previsto, ayudará a evitar confusiones cuando llegue a los detalles.
Un par de detalles seleccionados.
El ejecutor es llamado inmediatamente.
Un detalle importante es que la función ejecutora pasada al constructor Promise()
se llama inmediatamente (antes de que el constructor devuelva la promesa); mientras que las funciones del controlador pasadas al método then()
no serán llamadas hasta más tarde (si alguna vez).
Bergi mencionó esto, pero quería repetirlo sin usar los términos a / sincrónicamente, lo que puede confundirse si no está leyendo con atención: la distinción entre una función que llama a algo de forma asíncrona y que se le llama de forma asíncrona es fácil de ignorar en la comunicación .
resolve()
no está en onFulfill()
Un detalle más que me gustaría enfatizar, porque me confundió por un tiempo, es que las devoluciones de llamadas de resolve()
y de reject()
pasadas a la función ejecutora del constructor Promise()
no son las devoluciones de llamadas pasadas posteriormente al método then()
. Esto parece obvio en retrospectiva, pero la conexión aparente me hizo girar en círculos durante demasiado tiempo. Definitivamente hay una conexión, pero es una conexión suelta y dinámica.
En cambio, las devoluciones de llamada de resolve()
y reject()
son funciones proporcionadas por el "sistema" , y el constructor de Promise
las pasa a la función ejecutora cuando crea una promesa. Cuando se llama a la función resolve()
, se ejecuta el código del sistema que potencialmente cambia el estado de la promesa y, finalmente, conduce a una devolución de llamada onFulfilled()
se llama de forma asíncrona. ¡No piense en llamar a resolve()
como un contenedor ajustado para llamar a onFulfill()
!