Mise à jour (2017)
Ici en 2017, les promesses sont intégrées à JavaScript, elles ont été ajoutées par la spécification ES2015 (les polyfills sont disponibles pour les environnements obsolètes comme IE8-IE11). La syntaxe avec laquelle ils sont allés utilise un rappel que vous passez au Promise
constructeur (l' Promise
exécuteur ) qui reçoit les fonctions de résolution / rejet de la promesse comme arguments.
Premièrement, puisque async
maintenant a une signification en JavaScript (même si ce n'est qu'un mot-clé dans certains contextes), je vais utiliser later
comme nom de la fonction pour éviter toute confusion.
Délai de base
En utilisant des promesses natives (ou un polyfill fidèle), cela ressemblerait à ceci:
function later(delay) {
return new Promise(function(resolve) {
setTimeout(resolve, delay);
});
}
Notez que cela suppose une version de setTimeout
qui est conforme à la définition des navigateurs où setTimeout
ne transmet aucun argument au rappel à moins que vous ne les donniez après l'intervalle (cela peut ne pas être vrai dans les environnements sans navigateur, et ce n'était pas le cas auparavant vrai sur Firefox, mais c'est maintenant; c'est vrai sur Chrome et même de retour sur IE8).
Délai de base avec valeur
Si vous voulez que votre fonction passe éventuellement une valeur de résolution, sur n'importe quel navigateur vaguement moderne qui vous permet de donner des arguments supplémentaires setTimeout
après le délai, puis de les transmettre au rappel lorsqu'il est appelé, vous pouvez le faire (Firefox et Chrome actuels; IE11 + , probablement Edge; pas IE8 ou IE9, aucune idée 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 vous utilisez les fonctions fléchées ES2015 +, cela peut être plus concis:
function later(delay, value) {
return new Promise(resolve => setTimeout(resolve, delay, value));
}
ou même
const later = (delay, value) =>
new Promise(resolve => setTimeout(resolve, delay, value));
Retard annulable avec valeur
Si vous souhaitez rendre possible l'annulation du délai d'expiration, vous ne pouvez pas simplement renvoyer une promesse later
, car les promesses ne peuvent pas être annulées.
Mais nous pouvons facilement renvoyer un objet avec une cancel
méthode et un accesseur pour la promesse, et rejeter la promesse en cas d'annulation:
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;
}
}
};
};
Exemple en direct:
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);
Réponse originale de 2014
Habituellement, vous aurez une bibliothèque de promesses (celle que vous écrivez vous-même, ou l'une des nombreuses). Cette bibliothèque aura généralement un objet que vous pourrez créer et «résoudre» plus tard, et cet objet aura une «promesse» que vous pourrez en tirer.
Alors later
aurait tendance à ressembler à quelque chose comme ceci:
function later() {
var p = new PromiseThingy();
setTimeout(function() {
p.resolve();
}, 2000);
return p.promise(); // Note we're not returning `p` directly
}
Dans un commentaire sur la question, j'ai demandé:
Essayez-vous de créer votre propre bibliothèque de promesses?
et tu as dit
Je ne l'étais pas mais je suppose que c'est en fait ce que j'essayais de comprendre. Voilà comment une bibliothèque le ferait
Pour aider à cette compréhension, voici un exemple très très basique , qui n'est pas conforme à distance 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>