javascript asynchronous promise w3c

javascript - ¿Por qué el constructor Promise necesita un ejecutor?



asynchronous w3c (2)

Esto se llama el revelador patrón constructor acuñado por Domenic.

Básicamente, la idea es darle acceso a partes de un objeto mientras ese objeto aún no está completamente construido. Citando a Domenic:

Yo llamo a esto el patrón del constructor revelador porque el constructor Promise está revelando sus capacidades internas, pero solo al código que construye la promesa en cuestión. La capacidad de resolver o rechazar la promesa solo se revela al código de construcción, y de manera crucial no se revela a nadie que use la promesa. Entonces, si entregamos p a otro consumidor, digamos

El pasado

Inicialmente, las promesas funcionaban con objetos diferidos, esto es cierto en las promesas Twisted en las que se originaron las promesas de JavaScript. Esto sigue siendo cierto (pero a menudo en desuso) en implementaciones anteriores como $q , Q, jQuery de Angular y versiones antiguas de bluebird.

La API fue algo así como:

var d = Deferred(); d.resolve(); d.reject(); d.promise; // the actual promise

Funcionó, pero tenía un problema. Los diferidos y el constructor de promesas se usan generalmente para convertir API que no son promesas en promesas. Hay un problema "famoso" en JavaScript llamado Zalgo ; básicamente, significa que una API debe ser síncrona o asíncrona, pero nunca ambas cosas a la vez.

La cuestión es que con los diferidos es posible hacer algo como:

function request(param) { var d = Deferred(); var options = JSON.parse(param); d.ajax(function(err, value) { if(err) d.reject(err); else d.resolve(value); }); }

Aquí hay un error sutil oculto: si param no es un JSON válido, esta función se ejecuta sincrónicamente, lo que significa que tengo que ajustar cada función de devolución de promesa tanto en a } catch (e) { como en .catch(e => para capturar todo errores

El constructor de promesas capta tales excepciones y las convierte en rechazos, lo que significa que nunca tendrá que preocuparse por las excepciones síncronas frente a las asincrónicas con promesas. (Te protege del otro lado ejecutando siempre las devoluciones de llamada "en el siguiente tic").

Además, también requería un tipo adicional que cada desarrollador tiene que aprender sobre dónde no lo hace el constructor de la promesa, lo cual es bastante bueno.

Al usar Promises , ¿por qué no se pueden definir desencadenantes de resolve y reject en otra parte de la base de código?

No entiendo por qué la lógica de resolve y reject debe localizarse donde se declara la promesa. ¿Es esto un descuido o hay un beneficio al ordenar el parámetro executor ?

Creo que la función ejecutora debería ser opcional, y que su existencia debería determinar si la promesa encapsula la resolución o no. La promesa sería mucho más extensible sin tales mandatos, ya que no tiene que iniciar la sincronización de inmediato. La promesa también debe ser reiniciable. Es un interruptor de 1 disparo, 1 o 0, resolve() o reject() . Hay una multitud de resultados paralelos y secuenciales que se pueden adjuntar: promise.then(parallel1) y promise.then(parallel2) y también promise.then(seq1).then(seq2) pero los jugadores con privilegios de referencia no pueden resolver / rechazar INTO el interruptor

Puede construir un árbol de resultados más adelante, pero no puede alterarlos, ni puede alterar las raíces (disparadores de entrada)

Honestamente, el árbol de resultados secuenciales también debe ser editable ... digamos que desea unir un paso y hacer otra cosa, después de haber declarado muchas cadenas de promesa. No tiene sentido reconstruir la promesa y cada función secuencial, especialmente porque ni siquiera puedes rechazar o destruir la promesa ...


Para su información, si se muere por usar la interfaz diferida en lugar de la interfaz del ejecutor de Promise a pesar de todas las buenas razones contra la interfaz diferida, puede codificar una trivialmente una vez y luego usarla en todas partes (personalmente creo que es una mala idea codificar esto manera, pero su volumen de preguntas sobre este tema sugiere que piense de manera diferente, así que aquí está):

function Deferred() { var self = this; var p = this.promise = new Promise(function(resolve, reject) { self.resolve = resolve; self.reject = reject; }); this.then = this.promise.then.bind(p); this.catch = this.promise.catch.bind(p); }

Ahora, puede usar la interfaz que parece estar pidiendo:

var d = new Deferred(); d.resolve(); d.reject(); d.promise; // the actual promise d.then(...) // can use .then() on either the Deferred or the Promise d.promise.then(...)

Aquí una versión ES6 un poco más compacta:

function Deferred() { let p = this.promise = new Promise((resolve, reject) => { this.resolve = resolve; this.reject = reject; }); this.then = this.promise.then.bind(p); this.catch = this.promise.catch.bind(p); if (this.finally) { this.finally = this.promise.finally.bind(p); } }

O puede hacer lo que pidió en su pregunta utilizando este constructor Deferred() :

var request = new Deferred(); request.resolve(); request.then(handleSuccess, handleError);

Pero tiene las desventajas señaladas por Benjamin y no se considera la mejor manera de codificar las promesas.

Algo similar se muestra aquí en MDN .