una trabajar promises promesas promesa platzi nodejs node hacer google encadenamiento con como javascript promise

trabajar - Intento de implementación de promesa básica de Javascript



promise typescript (4)

(Para una implementación completa de Promise, desplácese hacia abajo).

Algunos problemas en tu código

Hay varios problemas, pero creo que el principal error en su código es que toma el argumento dado al método de then y lo pasa como argumento a una nueva promesa:

this.then=function(resolve,reject) { var nextPromise=new Aaa(resolve,true); // ...

Aunque ambos argumentos son funciones de devolución de llamada, tienen una firma diferente y tienen propósitos completamente diferentes:

  • El argumento para el constructor de promesa es una función de devolución de llamada que debe ejecutarse inmediatamente , sincrónicamente . Se le pasa una función como primer argumento, con la que puede resolver la promesa que está creando.
  • El (primer) argumento para el método the then , es una función de devolución de llamada que solo se ejecutará más tarde, de forma asincrónica, cuando se resuelva la promesa básica y a la que se pase el valor resuelto como argumento.

También puede ver la diferencia en su código, donde almacena el argumento para el constructor como la propiedad f . Tienes ambos esto:

t.thens[i].f(g);

... donde g es el valor resuelto, pero también esto:

this.f(this.resolve,this.reject);

... donde los argumentos son funciones. Cuando creas el próximo Promesa , de hecho, primero llamas a f con estos dos argumentos, y luego, con el argumento g .

Una implementación conforme a Promises / A + desde cero

Podríamos construir nuestra propia implementación de Promesa siguiendo los requisitos de la especificación Promises / A + :

2.1 Promesa de estados

Solo se permiten 2 transiciones de estado: de pendiente a cumplido y de pendiente a rechazado. Ninguna otra transición debería ser posible, y una vez que se ha realizado una transición, el valor de la promesa (o razón de rechazo) no debería cambiar.

Aquí hay una implementación simple que se apegará a las restricciones anteriores. Los comentarios hacen referencia a los requisitos numerados en la especificación anterior:

function MyPromise(executor) { this.state = ''pending''; this.value = undefined; executor(this.resolve.bind(this), this.reject.bind(this)); } // 2.1.1.1: provide only two ways to transition MyPromise.prototype.resolve = function (value) { if (this.state !== ''pending'') return; // 2.1.2.1, 2.1.3.1: cannot transition anymore this.state = ''fulfilled''; // 2.1.1.1: can transition this.value = value; // 2.1.2.2: must have a value } MyPromise.prototype.reject = function (reason) { if (this.state !== ''pending'') return; // 2.1.2.1, 2.1.3.1: cannot transition anymore this.state = ''rejected''; // 2.1.1.1: can transition this.value = reason; // 2.1.3.2: must have a reason }

Por supuesto, esto no proporciona el método de then , que es la clave de Promesas:

2.2 El método then

Este es el núcleo de la especificación. El código anterior se puede extender para exponer el método then , que devuelve una promesa y proporciona la ejecución asincrónica de la devolución de llamada apropiada en then , solo una vez, proporcionando múltiples llamadas en then , convirtiendo excepciones en rechazos, ... etc.

Entonces, el siguiente código agrega el método then , pero también una función de broadcast que se define por separado, porque debe invocarse en cualquier cambio de estado: esto no solo incluye el efecto del método then (se agrega una promesa a una lista), sino también de los métodos de resolve y reject (cambio de estado y valor).

function MyPromise(executor) { this.state = ''pending''; this.value = undefined; // A list of "clients" that need to be notified when a state // change event occurs. These event-consumers are the promises // that are returned by the calls to the `then` method. this.consumers = []; executor(this.resolve.bind(this), this.reject.bind(this)); } // 2.1.1.1: provide only two ways to transition MyPromise.prototype.resolve = function (value) { if (this.state !== ''pending'') return; // 2.1.2.1, 2.1.3.1: cannot transition anymore this.state = ''fulfilled''; // 2.1.1.1: can transition this.value = value; // 2.1.2.2: must have a value this.broadcast(); } MyPromise.prototype.reject = function (reason) { if (this.state !== ''pending'') return; // 2.1.2.1, 2.1.3.1: cannot transition anymore this.state = ''rejected''; // 2.1.1.1: can transition this.value = reason; // 2.1.3.2: must have a reason this.broadcast(); } // A promise’s then method accepts two arguments: MyPromise.prototype.then = function(onFulfilled, onRejected) { var consumer = new MyPromise(function () {}); // 2.2.1.1 ignore onFulfilled if not a function consumer.onFulfilled = typeof onFulfilled === ''function'' ? onFulfilled : null; // 2.2.1.2 ignore onRejected if not a function consumer.onRejected = typeof onRejected === ''function'' ? onRejected : null; // 2.2.6.1, 2.2.6.2: .then() may be called multiple times on the same promise this.consumers.push(consumer); // It might be that the promise was already resolved... this.broadcast(); // 2.2.7: .then() must return a promise return consumer; }; MyPromise.prototype.broadcast = function() { var promise = this; // 2.2.2.1, 2.2.2.2, 2.2.3.1, 2.2.3.2 called after promise is resolved if (this.state === ''pending'') return; // 2.2.6.1, 2.2.6.2 all respective callbacks must execute var callbackName = this.state == ''fulfilled'' ? ''onFulfilled'' : ''onRejected''; var resolver = this.state == ''fulfilled'' ? ''resolve'' : ''reject''; // 2.2.4 onFulfilled/onRejected must be called asynchronously setTimeout(function() { // 2.2.6.1, 2.2.6.2 traverse in order, 2.2.2.3, 2.2.3.3 called only once promise.consumers.splice(0).forEach(function(consumer) { try { var callback = consumer[callbackName]; // 2.2.1.1, 2.2.1.2 ignore callback if not a function, else // 2.2.5 call callback as plain function without context if (callback) { // TODO: 2.2.7.1. For now we simply fulfill the promise: consumer.resolve(callback(promise.value)); } else { // 2.2.7.3 resolve in same way as current promise consumer[resolver](promise.value); } } catch (e) { // 2.2.7.2 consumer.reject(e); }; }) }); };

Esto cubre casi todo, excepto que en el TODO: comentario, el llamado Procedimiento de Promesa de Resolución debe ser llamado:

2.3 El procedimiento de resolución de promesa

Este es un procedimiento que trata los valores que son mandables (o incluso promesas) de manera diferente: en lugar de devolver el valor tal como está, el procedimiento ejecutará el método de ese valor y asincrónicamente cumplirá la promesa con el valor recibido de esa devolución de llamada en ese then . No se menciona en las especificaciones, pero esto es interesante de realizar no solo en el método de then , sino también cuando la promesa principal se resuelve con dicho valor.

Por lo tanto, el método de resolve existente debe reemplazarse con este "Procedimiento de resolución de promesa", que llamará al original. El original podría llamarse "cumplir", para indicar que resolverá la promesa siempre cumplida:

function MyPromise(executor) { this.state = ''pending''; this.value = undefined; // A list of "clients" that need to be notified when a state // change event occurs. These event-consumers are the promises // that are returned by the calls to the `then` method. this.consumers = []; executor(this.resolve.bind(this), this.reject.bind(this)); } // 2.1.1.1: provide only two ways to transition MyPromise.prototype.fulfill = function (value) { if (this.state !== ''pending'') return; // 2.1.2.1, 2.1.3.1: cannot transition anymore this.state = ''fulfilled''; // 2.1.1.1: can transition this.value = value; // 2.1.2.2: must have a value this.broadcast(); } MyPromise.prototype.reject = function (reason) { if (this.state !== ''pending'') return; // 2.1.2.1, 2.1.3.1: cannot transition anymore this.state = ''rejected''; // 2.1.1.1: can transition this.value = reason; // 2.1.3.2: must have a reason this.broadcast(); } // A promise’s then method accepts two arguments: MyPromise.prototype.then = function(onFulfilled, onRejected) { var consumer = new MyPromise(function () {}); // 2.2.1.1 ignore onFulfilled if not a function consumer.onFulfilled = typeof onFulfilled === ''function'' ? onFulfilled : null; // 2.2.1.2 ignore onRejected if not a function consumer.onRejected = typeof onRejected === ''function'' ? onRejected : null; // 2.2.6.1, 2.2.6.2: .then() may be called multiple times on the same promise this.consumers.push(consumer); // It might be that the promise was already resolved... this.broadcast(); // 2.2.7: .then() must return a promise return consumer; }; MyPromise.prototype.broadcast = function() { var promise = this; // 2.2.2.1, 2.2.2.2, 2.2.3.1, 2.2.3.2 called after promise is resolved if (this.state === ''pending'') return; // 2.2.6.1, 2.2.6.2 all respective callbacks must execute var callbackName = this.state == ''fulfilled'' ? ''onFulfilled'' : ''onRejected''; var resolver = this.state == ''fulfilled'' ? ''resolve'' : ''reject''; // 2.2.4 onFulfilled/onRejected must be called asynchronously setTimeout(function() { // 2.2.6.1, 2.2.6.2 traverse in order, 2.2.2.3, 2.2.3.3 called only once promise.consumers.splice(0).forEach(function(consumer) { try { var callback = consumer[callbackName]; // 2.2.1.1, 2.2.1.2 ignore callback if not a function, else // 2.2.5 call callback as plain function without context if (callback) { // 2.2.7.1. execute the Promise Resolution Procedure: consumer.resolve(callback(promise.value)); } else { // 2.2.7.3 resolve in same way as current promise consumer[resolver](promise.value); } } catch (e) { // 2.2.7.2 consumer.reject(e); }; }) }); }; // The Promise Resolution Procedure: will treat values that are thenables/promises // and will eventually call either fulfill or reject/throw. MyPromise.prototype.resolve = function(x) { var wasCalled, then; // 2.3.1 if (this === x) { throw new TypeError(''Circular reference: promise value is promise itself''); } // 2.3.2 if (x instanceof MyPromise) { // 2.3.2.1, 2.3.2.2, 2.3.2.3 x.then(this.resolve.bind(this), this.reject.bind(this)); } else if (x === Object(x)) { // 2.3.3 try { // 2.3.3.1 then = x.then; if (typeof then === ''function'') { // 2.3.3.3 then.call(x, function resolve(y) { // 2.3.3.3.3 don''t allow multiple calls if (wasCalled) return; wasCalled = true; // 2.3.3.3.1 recurse this.resolve(y); }.bind(this), function reject(reasonY) { // 2.3.3.3.3 don''t allow multiple calls if (wasCalled) return; wasCalled = true; // 2.3.3.3.2 this.reject(reasonY); }.bind(this)); } else { // 2.3.3.4 this.fulfill(x); } } catch(e) { // 2.3.3.3.4.1 ignore if call was made if (wasCalled) return; // 2.3.3.2 or 2.3.3.3.4.2 this.reject(e); } } else { // 2.3.4 this.fulfill(x); } }

Esto ahora es compatible con Promises / A +, al menos pasa el test-suite. Sin embargo, el objeto Promesa expone demasiados métodos y propiedades:

Un Objeto Promesa con solo then

El constructor construido anteriormente crea algo que se parece más a un objeto diferido , es decir, que expone métodos de resolve y reject . Peor aún, las propiedades de status y value son editables. Por lo tanto, sería más lógico considerar esto como un constructor para un objeto diferido no seguro y crear un constructor Promise independiente que se base en eso, pero solo exponga lo que se necesita: un método y una devolución de llamada del constructor que pueden acceder a resolve y reject .

El objeto diferido puede entonces prescindir del argumento callback del constructor y proporcionar acceso al objeto promesa puro a través de una propiedad promise :

function Deferred() { this.state = ''pending''; this.value = undefined; this.consumers = []; this.promise = Object.create(MyPromise.prototype, { then: { value: this.then.bind(this) } }); } // 2.1.1.1: provide only two ways to transition Deferred.prototype.fulfill = function (value) { if (this.state !== ''pending'') return; // 2.1.2.1, 2.1.3.1: cannot transition anymore this.state = ''fulfilled''; // 2.1.1.1: can transition this.value = value; // 2.1.2.2: must have a value this.broadcast(); } Deferred.prototype.reject = function (reason) { if (this.state !== ''pending'') return; // 2.1.2.1, 2.1.3.1: cannot transition anymore this.state = ''rejected''; // 2.1.1.1: can transition this.value = reason; // 2.1.3.2: must have a reason this.broadcast(); } // A promise’s then method accepts two arguments: Deferred.prototype.then = function(onFulfilled, onRejected) { var consumer = new Deferred(); // 2.2.1.1 ignore onFulfilled if not a function consumer.onFulfilled = typeof onFulfilled === ''function'' ? onFulfilled : null; // 2.2.1.2 ignore onRejected if not a function consumer.onRejected = typeof onRejected === ''function'' ? onRejected : null; // 2.2.6.1, 2.2.6.2: .then() may be called multiple times on the same promise this.consumers.push(consumer); // It might be that the promise was already resolved... this.broadcast(); // 2.2.7: .then() must return a promise return consumer; }; Deferred.prototype.broadcast = function() { var promise = this; // 2.2.2.1, 2.2.2.2, 2.2.3.1, 2.2.3.2 called after promise is resolved if (this.state === ''pending'') return; // 2.2.6.1, 2.2.6.2 all respective callbacks must execute var callbackName = this.state == ''fulfilled'' ? ''onFulfilled'' : ''onRejected''; var resolver = this.state == ''fulfilled'' ? ''resolve'' : ''reject''; // 2.2.4 onFulfilled/onRejected must be called asynchronously setTimeout(function() { // 2.2.6.1, 2.2.6.2 traverse in order, 2.2.2.3, 2.2.3.3 called only once promise.consumers.splice(0).forEach(function(consumer) { try { var callback = consumer[callbackName]; // 2.2.1.1, 2.2.1.2 ignore callback if not a function, else // 2.2.5 call callback as plain function without context if (callback) { // 2.2.7.1. execute the Promise Resolution Procedure: consumer.resolve(callback(promise.value)); } else { // 2.2.7.3 resolve in same way as current promise consumer[resolver](promise.value); } } catch (e) { // 2.2.7.2 consumer.reject(e); }; }) }); }; // The Promise Resolution Procedure: will treat values that are thenables/promises // and will eventually call either fulfill or reject/throw. Deferred.prototype.resolve = function(x) { var wasCalled, then; // 2.3.1 if (this.promise === x) { throw new TypeError(''Circular reference: promise value is promise itself''); } // 2.3.2 if (x instanceof MyPromise) { // 2.3.2.1, 2.3.2.2, 2.3.2.3 x.then(this.resolve.bind(this), this.reject.bind(this)); } else if (x === Object(x)) { // 2.3.3 try { // 2.3.3.1 then = x.then; if (typeof then === ''function'') { // 2.3.3.3 then.call(x, function resolve(y) { // 2.3.3.3.3 don''t allow multiple calls if (wasCalled) return; wasCalled = true; // 2.3.3.3.1 recurse this.resolve(y); }.bind(this), function reject(reasonY) { // 2.3.3.3.3 don''t allow multiple calls if (wasCalled) return; wasCalled = true; // 2.3.3.3.2 this.reject(reasonY); }.bind(this)); } else { // 2.3.3.4 this.fulfill(x); } } catch(e) { // 2.3.3.3.4.1 ignore if call was made if (wasCalled) return; // 2.3.3.2 or 2.3.3.3.4.2 this.reject(e); } } else { // 2.3.4 this.fulfill(x); } } function MyPromise(executor) { // A Promise is just a wrapper around a Deferred, exposing only the `then` // method, while `resolve` and `reject` are available in the constructor callback var df = new Deferred(); // Provide access to the `resolve` and `reject` methods via the callback executor(df.resolve.bind(df), df.reject.bind(df)); return df.promise; }

Hay varias optimizaciones posibles para este código, como hacer que los métodos Diferidos sean funciones privadas, y fusionar un código similar en bloques de código más cortos, pero tal como está ahora muestra claramente dónde se cubre cada requisito.

Feliz codificación.

Para obtener una mejor comprensión de cómo funcionan las promesas en Javascript, decidí probarlo y codificar la implementación básica.

Básicamente quiero implementar Promises Object (lo llamo Aaa en mi código) que toma la función como argumento. Esta función puede llamar a resolver para resolve la promesa, o rechazar reject . La implementación y el uso básicos están a continuación. No estoy seguro de si el segundo argumento es aceptable de acuerdo con las especificaciones de la promesa, pero eso es lo que obtuve hasta ahora.

Aaa=function(f,pause) { console.log("ggg"); var t=this; this.f=f; this.thens=[]; this.resolve=function(g) { for(var i=0;i<t.thens.length;i++) { // try/catch to be used later for dealing with exceptions try { t.thens[i].f(g); t.thens[i].resolve(); } catch(ex) {} } }; // to be implemented later this.reject=function(g) {}; this.then=function(resolve,reject) { // i''m passing true for pause argument as we dont need to execute promise code just yet var nextPromise=new Aaa(resolve,true); this.thens.push(nextPromise); return nextPromise; } if(!pause) this.f(this.resolve,this.reject); } var aaa=new Aaa(function(resolve,reject) { console.log("aaa"); setTimeout(function() { console.log("fff"); resolve("good"); },2000); console.log("bbb"); });

Entonces ahora la promesa puede ser creada, llamada y resuelta. Cada método then devolverá un nuevo Aaa (Promesa) para que puedan encadenarse. Ahora el código a continuación usa la promesa creada anteriormente y las cadenas then devoluciones de llamada. Cada uno devuelve una nueva promesa y en este caso parece funcionar bien:

aaa.then(function(res) { console.log("ccc"); console.log(res); }) .then(function(res) { console.log("ddd"); console.log(res); },function(rej) { console.log("eee"); console.log(rej); });

el resultado que obtengo es:

ggg aaa bbb ggg ggg fff ccc good ddd undefined

El problema es, sin embargo, cuando una de las llamadas devuelve una promesa:

aaa.then(function(res) { console.log("ccc"); console.log(res); // here we return the promise manually. then next then call where "ddd" is output should not be called UNTIL this promise is resolved. How to do that? return new Aaa(function(resolve,reject) { console.log("iii"); setTimeout(function() { console.log("kkk"); resolve("good2"); // reject("bad"); },2000); console.log("jjj"); }).then(function (res) { console.log("lll"); console.log(res); }); }) .then(function(res) { console.log("ddd"); console.log(res); },function(rej) { console.log("eee"); console.log(rej); });

El resultado es:

ggg aaa bbb ggg ggg fff ccc good ggg iii jjj ggg ddd undefined kkk lll good2

La llamada a la que se ddd no debe llamarse HASTA que se resuelva la promesa devuelta que acabamos de agregar.

¿Cómo se implementaría mejor?


Hay varios casos que no manejas aquí. La mejor apuesta es comenzar construyendo la promesa como una máquina de estado:

var PENDING = 0; var FULFILLED = 1; var REJECTED = 2; function Promise() { // store state which can be PENDING, FULFILLED or REJECTED var state = PENDING; // store value once FULFILLED or REJECTED var value = null; // store sucess & failure handlers var handlers = []; }

Ahora definamos un simple ayudante para usar durante el resto de nuestra implementación:

// a function that returns `then` if `value` is a promise, otherwise `null` function getThen(value) { if (result && (typeof result === ''object'' || typeof result === ''function'')) { var then = value.then; if (typeof then === ''function'') { return then; } } return null; }

A continuación, debemos considerar cada una de las transformaciones que pueden ocurrir:

var PENDING = 0; var FULFILLED = 1; var REJECTED = 2; function Promise() { // store state which can be PENDING, FULFILLED or REJECTED var state = PENDING; // store value once FULFILLED or REJECTED var value = null; // store sucess & failure handlers var handlers = []; function resolve(result) { try { var then = getThen(result); if (then) { doResolve(then.bind(result), resolve, reject) return } state = FULFILLED; value = result; } catch (e) { reject(e); } } function reject(error) { state = REJECTED; value = error; } }

Observe cómo la resolve puede recibir una Promesa como su argumento, pero una Promesa nunca se puede cumplir con otra Promesa. Entonces tenemos que manejar este caso especial.

Tenga en cuenta también que una Promesa solo puede cumplirse / rechazarse una vez. También tenemos el problema de que una promesa de un tercero puede portarse mal, y debemos proteger nuestro código contra eso. Por esta razón, no acabo de llamar a result.then(resolve, reject) desde dentro de la resolve . En cambio, lo dividí en una función separada:

/** * Take a potentially misbehaving resolver function and make sure * onFulfilled and onRejected are only called once. * * Makes no guarantees about asynchrony. */ function doResolve(fn, onFulfilled, onRejected) { var done = false; try { fn(function (value) { if (done) return done = true onFulfilled(value) }, function (reason) { if (done) return done = true onRejected(reason) }) } catch (ex) { if (done) return done = true onRejected(ex) } }

Entonces ahora tenemos una máquina de estado completa, pero no hay forma de observar o activar los cambios de estado. Comencemos agregando una forma de activar los cambios de estado pasando una función de resolver.

function Promise(fn) { if (typeof this !== ''object'') throw new TypeError(''Promises must be constructed via new''); if (typeof fn !== ''function'') throw new TypeError(''fn must be a function''); // store state which can be PENDING, FULFILLED or REJECTED var state = PENDING; // store value once FULFILLED or REJECTED var value = null; // store sucess & failure handlers var handlers = []; function resolve(result) { try { var then = getThen(result); if (then) { doResolve(then.bind(result), resolve, reject) return } state = FULFILLED; value = result; } catch (e) { reject(e); } } function reject(error) { state = REJECTED; value = error; } doResolve(fn, resolve, reject); }

Como puede ver, reutilizamos doResolve porque tenemos otro resolver no confiable. El fn podría llamar a resolve o reject varias veces, y podría arrojar un error. Necesitamos manejar todos estos casos (y eso es lo que hace doResolve ).

Ahora tenemos la máquina de estado completada, pero no hemos expuesto ninguna información sobre el estado en el que se encuentra. Intentemos agregar un .done(onFulfilled, onRejected) que sea como .then excepto que no devuelve una Promesa y no maneja los errores arrojados por onFulfilled y onRejected .

var PENDING = 0; var FULFILLED = 1; var REJECTED = 2; function Promise(fn) { if (typeof this !== ''object'') throw new TypeError(''Promises must be constructed via new''); if (typeof fn !== ''function'') throw new TypeError(''fn must be a function''); // store state which can be PENDING, FULFILLED or REJECTED var state = PENDING; // store value once FULFILLED or REJECTED var value = null; // store sucess & failure handlers var handlers = []; function resolve(result) { try { var then = getThen(result); if (then) { doResolve(then.bind(result), resolve, reject) return } state = FULFILLED; value = result; handlers.forEach(handle); handlers = null; } catch (e) { reject(e); } } function reject(error) { state = REJECTED; value = error; handlers.forEach(handle); handlers = null; } function handle(handler) { if (state === PENDING) { handlers.push(handler); } else { if (state === FULFILLED && typeof handler.onFulfilled === ''function'') { handler.onFulfilled(value); } if (state === REJECTED && typeof handler.onRejected === ''function'') { handler.onRejected(value); } } } this.done = function (onFulfilled, onRejected) { setTimeout(function () { // ensure we are always asynchronous handle({ onFulfilled: onFulfilled, onRejected: onRejected }); }, 0); } doResolve(fn, resolve, reject); }

Tenga en cuenta cómo debemos manejar el caso de que .done sea ​​llamado tanto antes como después de que la Promesa sea cumplida / rechazada.

Casi tenemos una implementación prometedora completa, pero, como ya .then notado al crear tu propia implementación, necesitamos un método .then que devuelva una Promesa.

Podemos construir esto fácilmente desde .done :

this.then = function (onFulfilled, onRejected) { var self = this; return new Promise(function (resolve, reject) { return self.done(function (result) { if (typeof onFulfilled === ''function'') { try { return resolve(onFulfilled(result)); } catch (ex) { return reject(ex); } } else { return resolve(result); } }, function (error) { if (typeof onRejected === ''function'') { try { return resolve(onRejected(error)); } catch (ex) { return reject(ex); } } else { return reject(error); } }); }); }

Tenga en cuenta aquí cómo obtenemos lo que estaba luchando gratis ahora, porque resolve acepta una Promesa y espera a que se resuelva.

NB: No he probado esta implementación Promise (aunque es correcta según mi leal saber y entender). Debe probar cualquier implementación que cree contra el paquete de pruebas Promises / A + ( https://github.com/promises-aplus/promises-tests ) y también puede encontrar las especificaciones de Promises / A + ( https://github.com/promises-aplus/promises-spec ) útil para averiguar cuál es el comportamiento correcto para cualquier parte específica del algoritmo. Como recurso final, la promise es una implementación muy mínima de la especificación Promise.


todo esto parece extremadamente complicado. Creo que hay una solución recursiva realmente simple. Voy a omitir el rechazo en aras de la brevedad, pero es más o menos lo mismo que resolver, excepto que se detiene la cadena.

var MyPromise = function(callback) { this.callbacks = []; callback(this.resolve.bind(this)); } MyPromise.prototype.resolve = function(data) { var callback = this.callbacks.pop(); var result = callback(data); if (!result) return; if (result instanceof MyPromise) { var resolve = this.resolve.bind(this); return result.then(function(d) { return resolve(d); }); } return this.resolve(result); } MyPromise.prototype.then = function(callback) { this.callbacks.unshift(callback); return this; }


mi solución

function Promise(resolver){ if(typeof resolver !== ''function'') { throw new TypeError(`Promise resolver ${resolver} is not a function`) } this.state = ''pending'' this.value = void 0 try{ resolver(this.resolve.bind(this), this.reject.bind(this)) }catch(error){ this.reject.call(this,error) } } Promise.prototype.resolve = function(value) { if(this.state !== ''pending'') return this.value = value this.state = ''fulfilled'' setTimeout( () => { if(!this.onFulfilled) return this.onFulfilled(value) }, 0) }; Promise.prototype.reject = function(reason){ if(this.state !== ''pending'') return this.value = reason this.state = ''rejected'' setTimeout( () => { if(this.onRejected){ this.onRejected(reason) }else{ throw `Uncaught (in promise) ${reason}` } }, 0) }; Promise.prototype.then = function(fulfilled, rejected){ if ( typeof fulfilled !== ''function'' && typeof rejected !== ''function'' ) { return this; } if (typeof fulfilled !== ''function'' && this.state === ''fulfilled'' || typeof rejected !== ''function'' && this.state === ''rejected'') { return this; } var self = this return new Promise( (resolve, reject) => { if(fulfilled && typeof fulfilled == "function"){ var onFulfilled = function (){ try{ var result = fulfilled(self.value) if(result && typeof result.then === ''function''){ result.then(resolve, reject) }else{ resolve(result) } }catch(error){ reject(error) } } if(self.state === ''pending''){ self.onFulfilled = onFulfilled }else if(self.state === ''fulfilled''){ onFulfilled() } } if(rejected && typeof rejected == "function"){ var onRejected = function (){ try{ var result = rejected(self.value) if(result && typeof result.then === ''function''){ result.then(resolve, reject) }else{ resolve(result) } }catch(error){ reject(error) } } if( self.state === ''pending''){ self.onRejected = onRejected }else if(self.state === ''rejected''){ onRejected() } } }) } /* * the methods don''t in Promise/A+ */ Promise.prototype.catch = function(onRejected){ return this.then(null, onRejected) } Promise.all = function(iterable){ if(typeof iterable[Symbol.iterator] !== ''function''){ throw new TypeError(`${iterable[Symbol.iterator]} is not a function`) } // Array,TypedArray,String,arguments ==> length; Map,Set ==> size let len = [...iterable].length, i = 0, counter = 0, res = []; return new Promise( (resolve, reject) => { for(let item of iterable){ ( (i) => { Promise.resolve(item).then(function(value){ counter++ res[i] = value if(counter == len){ resolve(res) } },function(reason){ if(!called){ reject(reason) } }) })(i++) } }) } Promise.race = function(iterable){ if(typeof iterable[Symbol.iterator] !== ''function''){ throw new TypeError(`${iterable[Symbol.iterator]} is not a function`) } return new Promise( (resolve,reject) => { for(let item of iterable){ Promise.resolve(item).then(function(value){ resolve(value) },function(reason){ reject(reason) }) } }) } Promise.resolve = function(value){ //if(value instanceof this) return value //if(value instanceof Promise) return value if(value.constructor !== Promise) return value return new Promise( (resolve,reject) => { if(value && typeof value === ''object'' && typeof value.then === ''function''){ resolve( value.then( v => v)) }else{ resolve(value) } }) } Promise.reject = function(reason){ return new Promise( (resolve,reject) => { reject(reason) }) }