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 (el Promise
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);
});
}
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();
}
En un comentario sobre la pregunta, pregunté:
¿Está intentando crear su propia biblioteca de promesas?
y tú dijiste
No lo estaba, pero supongo que ahora eso es realmente lo que estaba tratando de entender. Así como lo haría una biblioteca
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() {
var PromiseThingy = (function() {
function triggerCallback(callback, promise) {
try {
callback(promise.resolvedValue);
}
catch (e) {
}
}
function Promise() {
this.callbacks = [];
}
Promise.prototype.then = function(callback) {
var thispromise = this;
if (!this.resolved) {
this.callbacks.push(callback);
}
else {
setTimeout(function() {
triggerCallback(callback, thispromise);
}, 0);
}
return this;
};
function PromiseThingy() {
this.p = new 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);
}
}
};
PromiseThingy.prototype.promise = function() {
return this.p;
};
return PromiseThingy;
})();
function later() {
var p = new PromiseThingy();
setTimeout(function() {
p.resolve();
}, 2000);
return p.promise();
}
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>