Este no es un problema del mundo real, solo estoy tratando de entender cómo se crean las promesas.
Necesito entender cómo hacer una promesa para una función que no devuelve nada, como setTimeout.
Supongamos que tengo:
function async(callback){
setTimeout(function(){
callback();
}, 5000);
}
async(function(){
console.log('async called back');
});
¿Cómo creo una promesa que async
pueda volver después de que setTimeout
esté lista callback()
?
Supuse que envolverlo me llevaría a algún lado:
function setTimeoutReturnPromise(){
function promise(){}
promise.prototype.then = function() {
console.log('timed out');
};
setTimeout(function(){
return ???
},2000);
return promise;
}
Pero no puedo pensar más allá de esto.
javascript
settimeout
promise
reflejo
fuente
fuente
async function async(){...}
Respuestas:
Actualización (2017)
Aquí, en 2017, las promesas están integradas en JavaScript, fueron agregadas por la especificación ES2015 (los polyfills están disponibles para entornos obsoletos como IE8-IE11). La sintaxis que utilizaron usa una devolución de llamada que se pasa al
Promise
constructor (elPromise
ejecutor ) que recibe las funciones para resolver / rechazar la promesa como argumentos.Primero, dado que
async
ahora tiene un significado en JavaScript (aunque es solo una palabra clave en ciertos contextos), voy a usarlater
como nombre de la función para evitar confusiones.Retraso básico
Usando promesas nativas (o un polyfill fiel) se vería así:
function later(delay) { return new Promise(function(resolve) { setTimeout(resolve, delay); }); }
Tenga en cuenta que eso supone una versión
setTimeout
compatible con la definición de navegadores dondesetTimeout
no pasa ningún argumento a la devolución de llamada a menos que los proporcione después del intervalo (esto puede no ser cierto en entornos que no son de navegador, y no solía ser así cierto en Firefox, pero lo es ahora; es cierto en Chrome e incluso en IE8).Retraso básico con valor
Si desea que su función pase opcionalmente un valor de resolución, en cualquier navegador vagamente moderno que le permita dar argumentos adicionales
setTimeout
después de la demora y luego pasarlos a la devolución de llamada cuando se llame, puede hacerlo (Firefox y Chrome actuales; IE11 + , presumiblemente Edge; no IE8 o IE9, no tengo idea de IE10):function later(delay, value) { return new Promise(function(resolve) { setTimeout(resolve, delay, value); // Note the order, `delay` before `value` /* Or for outdated browsers that don't support doing that: setTimeout(function() { resolve(value); }, delay); Or alternately: setTimeout(resolve.bind(null, value), delay); */ }); }
Si está utilizando las funciones de flecha ES2015 +, eso puede ser más conciso:
function later(delay, value) { return new Promise(resolve => setTimeout(resolve, delay, value)); }
o incluso
const later = (delay, value) => new Promise(resolve => setTimeout(resolve, delay, value));
Retraso cancelable con valor
Si desea que sea posible cancelar el tiempo de espera, no puede simplemente devolver una promesa de
later
, porque las promesas no se pueden cancelar.Pero podemos devolver fácilmente un objeto con un
cancel
método y un descriptor de acceso para la promesa, y rechazar la promesa al cancelar:const later = (delay, value) => { let timer = 0; let reject = null; const promise = new Promise((resolve, _reject) => { reject = _reject; timer = setTimeout(resolve, delay, value); }); return { get promise() { return promise; }, cancel() { if (timer) { clearTimeout(timer); timer = 0; reject(); reject = null; } } }; };
Ejemplo en vivo:
const later = (delay, value) => { let timer = 0; let reject = null; const promise = new Promise((resolve, _reject) => { reject = _reject; timer = setTimeout(resolve, delay, value); }); return { get promise() { return promise; }, cancel() { if (timer) { clearTimeout(timer); timer = 0; reject(); reject = null; } } }; }; const l1 = later(100, "l1"); l1.promise .then(msg => { console.log(msg); }) .catch(() => { console.log("l1 cancelled"); }); const l2 = later(200, "l2"); l2.promise .then(msg => { console.log(msg); }) .catch(() => { console.log("l2 cancelled"); }); setTimeout(() => { l2.cancel(); }, 150);
Respuesta original de 2014
Por lo general, tendrá una biblioteca de promesas (una que escriba usted mismo o una de las varias que hay). Esa biblioteca generalmente tendrá un objeto que puede crear y luego "resolver", y ese objeto tendrá una "promesa" que puede obtener de él.
Entonces
later
tendería a verse algo como esto:function later() { var p = new PromiseThingy(); setTimeout(function() { p.resolve(); }, 2000); return p.promise(); // Note we're not returning `p` directly }
En un comentario sobre la pregunta, pregunté:
y tú dijiste
Para ayudar a esa comprensión, aquí hay un ejemplo muy básico , que no cumple remotamente con Promises-A: Live Copy
<!DOCTYPE html> <html> <head> <meta charset=utf-8 /> <title>Very basic promises</title> </head> <body> <script> (function() { // ==== Very basic promise implementation, not remotely Promises-A compliant, just a very basic example var PromiseThingy = (function() { // Internal - trigger a callback function triggerCallback(callback, promise) { try { callback(promise.resolvedValue); } catch (e) { } } // The internal promise constructor, we don't share this function Promise() { this.callbacks = []; } // Register a 'then' callback Promise.prototype.then = function(callback) { var thispromise = this; if (!this.resolved) { // Not resolved yet, remember the callback this.callbacks.push(callback); } else { // Resolved; trigger callback right away, but always async setTimeout(function() { triggerCallback(callback, thispromise); }, 0); } return this; }; // Our public constructor for PromiseThingys function PromiseThingy() { this.p = new Promise(); } // Resolve our underlying promise PromiseThingy.prototype.resolve = function(value) { var n; if (!this.p.resolved) { this.p.resolved = true; this.p.resolvedValue = value; for (n = 0; n < this.p.callbacks.length; ++n) { triggerCallback(this.p.callbacks[n], this.p); } } }; // Get our underlying promise PromiseThingy.prototype.promise = function() { return this.p; }; // Export public return PromiseThingy; })(); // ==== Using it function later() { var p = new PromiseThingy(); setTimeout(function() { p.resolve(); }, 2000); return p.promise(); // Note we're not returning `p` directly } display("Start " + Date.now()); later().then(function() { display("Done1 " + Date.now()); }).then(function() { display("Done2 " + Date.now()); }); function display(msg) { var p = document.createElement('p'); p.innerHTML = String(msg); document.body.appendChild(p); } })(); </script> </body> </html>
fuente
const setTimeoutAsync = (cb, delay) => new Promise((resolve) => { setTimeout(() => { resolve(cb()); }, delay); });
Podemos pasar 'cb fxn' personalizado como este 👆🏽
fuente
Esta no es una respuesta a la pregunta original. Pero, como una pregunta original no es un problema del mundo real, no debería ser un problema. Traté de explicarle a un amigo qué son las promesas en JavaScript y la diferencia entre promesa y devolución de llamada.
El siguiente código sirve como explicación:
//very basic callback example using setTimeout //function a is asynchronous function //function b used as a callback function a (callback){ setTimeout (function(){ console.log ('using callback:'); let mockResponseData = '{"data": "something for callback"}'; if (callback){ callback (mockResponseData); } }, 2000); } function b (dataJson) { let dataObject = JSON.parse (dataJson); console.log (dataObject.data); } a (b); //rewriting above code using Promise //function c is asynchronous function function c () { return new Promise(function (resolve, reject) { setTimeout (function(){ console.log ('using promise:'); let mockResponseData = '{"data": "something for promise"}'; resolve(mockResponseData); }, 2000); }); } c().then (b);
JsFiddle
fuente