C'est le début d'une nouvelle année, et alors que beaucoup de gens promettent d'être plus actifs, je vais vous montrer comment faire des Promise
d'être plus paresseux…Promise
s, c'est.
Cela aura plus de sens dans un instant.
Tout d'abord, regardons un exemple de base de Promise
. Ici, j'ai une fonction appelée sleep qui prend un temps en millisecondes et une valeur. Il renvoie une promesse qui exécutera un setTimeout
pendant le nombre de millisecondes que nous devrions attendre ; puis la promesse se résout avec la valeur.
/** * @template ValueType * @param {number} ms * @param {ValueType} value * @returns {Promise<ValueType>} */ function sleep(ms, value) { return new Promise((resolve) => { setTimeout(() => resolve(value), ms); }); }
Cela fonctionne comme ceci :
Nous pouvons attendre la fonction sleep
avec les arguments 1000
et 'Yawn & stretch'
, et après une seconde, la console
enregistrera la chaîne, 'Yawn & stretch'.
Il n'y a rien de trop spécial à ce sujet. Il se comporte probablement comme vous vous en doutez, mais cela devient un peu bizarre si nous le stockons comme une variable à await
plus tard, plutôt que d' await
tout de suite la Promise
renvoyée.
const nap = sleep(1000, 'Yawn & stretch')
Maintenant, disons que nous faisons un autre travail qui prend du temps (comme taper l'exemple suivant), puis await
la variable nap
.
Vous pouvez vous attendre à un délai d'une seconde avant la résolution, mais en fait, il se résout immédiatement. Chaque fois que vous créez une Promise
, vous instanciez la fonctionnalité asynchrone dont elle est responsable.
Dans notre exemple, au moment où nous définissons la variable nap
, la Promise
est créée qui exécute le setTimeout
. Parce que je suis un type lent, la Promise
sera résolue au moment où nous l' await
.
En d'autres termes, les Promise
sont impatientes. Ils n'attendent pas que vous les await
.
Dans certains cas, c'est une bonne chose. Dans d'autres cas, cela pourrait entraîner une utilisation inutile des ressources. Pour ces scénarios, vous voudrez peut-être quelque chose qui ressemble à une Promise
, mais qui utilise
Avant de continuer, je veux vous montrer quelque chose d'intéressant.
Les Promise
ne sont pas les seules choses qui peuvent être await
en JavaScript. Si nous créons un Object
simple avec une méthode .then()
, nous pouvons en fait await
cet objet comme n'importe quel Promise
.
C'est un peu bizarre, mais cela nous permet également de créer différents objets qui ressemblent à Promise
, mais qui n'en sont pas. Ces objets sont parfois appelés "
Dans cet esprit, créons un nouveauLazyPromise
qui étend le constructeur Promise
intégré. L'extension de la promesse n'est pas strictement nécessaire, mais elle la fait ressembler davantage à une Promise
en utilisant des choses comme instanceof
.
class LazyPromise extends Promise { /** @param {ConstructorParameters<PromiseConstructor>[0]} executor */ constructor(executor) { super(executor); if (typeof executor !== 'function') { throw new TypeError(`LazyPromise executor is not a function`); } this._executor = executor; } then() { this.promise = this.promise || new Promise(this._executor); return this.promise.then.apply(this.promise, arguments); } }
La partie sur laquelle se concentrer est la méthode then()
. Il détourne le comportement par défaut d'une Promise
standard d'attendre que la méthode .then()
soit exécutée avant de créer une vraie Promise
.
Cela évite d'instancier la fonctionnalité asynchrone jusqu'à ce que vous l'appeliez réellement. Et cela fonctionne si vous appelez explicitement .then()
ou utilisez await
.
Voyons maintenant ce qui se passe si nous remplaçons la Promise
dans la fonction sleep
d'origine par une LazyPromise
. Encore une fois, nous assignerons le résultat à une variable nap
.
function sleep(ms, value) { return new LazyPromise((resolve) => { setTimeout(() => resolve(value), ms); }); } const nap = sleep(1000, 'Yawn & stretch')
Ensuite, nous prenons notre temps pour taper la ligne d' await nap
et l'exécuter.
Cette fois, nous constatons un délai d'une seconde avant la résolution de la Promise
, quel que soit le temps écoulé depuis la création de la variable.
(Notez que cette implémentation ne crée la nouvelle Promise
qu'une seule fois et la référence dans les appels suivants. Donc, si nous devions l' await
à nouveau, elle se résoudrait immédiatement comme n'importe quelle Promise
normale)
Bien sûr, il s'agit d'un exemple trivial que vous ne trouverez probablement pas dans le code de production, mais de nombreux projets utilisent des objets de type Promise
évalués paresseusement. L'exemple le plus courant est probablement celui des bases de données ORM et des constructeurs de requêtes tels que
Considérez le pseudo-code ci-dessous. Il s'inspire de certains de ces générateurs de requête :
const query = db('user') .select('name') .limit(10) const users = await query
Nous créons une requête de base de données qui va à la table "user"
et sélectionne les dix premières entrées et renvoie leurs noms. En théorie, cela fonctionnerait bien avec une Promise
régulière.
Mais que se passe-t-il si nous voulons modifier la requête en fonction de certaines conditions telles que les paramètres de chaîne de requête ? Ce serait bien de pouvoir continuer à modifier la requête avant d'attendre finalement la Promise
.
const query = db('user') .select('name') .limit(10) if (orderBy) { query.orderBy(orderBy) } if (limit) { query.limit(limit) } if (id) { query.where({ id: id }) } const users = await query
Si la requête de base de données d'origine était une Promise
standard, elle instancierait la requête avec impatience dès que nous aurions attribué la variable, et nous ne serions pas en mesure de la modifier ultérieurement.
Avec l'évaluation paresseuse, nous pouvons écrire un code comme celui-ci qui est plus facile à suivre, améliore l'expérience du développeur et n'exécute la requête qu'une seule fois lorsque nous en avons besoin.
C'est un exemple où l'évaluation paresseuse est excellente. Cela peut également être utile pour des choses comme la création, la modification et l'orchestration des requêtes HTTP.
Les Promise
paresseuses sont très cool pour les bons cas d'utilisation, mais cela ne veut pas dire qu'elles devraient remplacer toutes les Promise
. Dans certains cas, il est avantageux d'instancier avec impatience et d'avoir la réponse prête dès que possible.
C'est un autre de ces scénarios "ça dépend". Mais la prochaine fois que quelqu'un vous demandera de faire une Promise
, pensez à être paresseux ( ͡° ͜ʖ ͡°).
Merci beaucoup d'avoir lu. Si vous avez aimé cet article, n'hésitez pas
Initialement publié le