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)
})
}