web-dev-qa-db-fra.com

Créer une promesse (ES6) sans commencer à la résoudre

À l'aide des promesses ES6, comment puis-je créer une promesse sans définir la logique de sa résolution? Voici un exemple de base (certains TypeScript):

var promises = {};
function waitFor(key: string): Promise<any> {
  if (key in promises) {
    return promises[key];
  }
  var promise = new Promise(resolve => {
    // But I don't want to try resolving anything here :(
  });

  promises[key] = promise;
  return promise;
}

function resolveWith(key: string, value: any): void {
  promises[key].resolve(value); // Not valid :(
}

Cela se fait facilement avec d'autres bibliothèques de promesses. JQuery, par exemple:

var deferreds = {};
function waitFor(key: string): Promise<any> {
  if (key in promises) {
    return deferreds[key].promise();
  }
  var def = $.Deferred();    
  deferreds[key] = def;
  return def.promise();
}

function resolveWith(key: string, value: any): void {
  deferreds[key].resolve(value);
}

La seule façon pour moi de le faire serait de stocker la fonction de résolution quelque part dans l'exécuteur de la promesse, mais cela semble compliqué, et je ne suis pas sûr qu'elle soit définie quand exactement cette fonction est exécutée - est-elle toujours exécutée immédiatement lors de la construction?

Merci.

53
Barguast

Bonne question!

Le résolveur transmis au constructeur de promesses s'exécute intentionnellement de manière synchrone afin de prendre en charge ce cas d'utilisation:

var deferreds = [];
var p = new Promise(function(resolve, reject){
    deferreds.Push({resolve: resolve, reject: reject});
});

Puis, à un moment ultérieur:

 deferreds[0].resolve("Hello"); // resolve the promise with "Hello"

La raison pour laquelle le constructeur de promesse est donné est que:

  • La logique de résolution est généralement (mais pas toujours) liée à la création.
  • Le constructeur de promesses est throw safe et convertit les exceptions en rejets.

Parfois, il ne convient pas et pour cela, le résolveur fonctionne de manière synchrone. Voici une lecture connexe sur le sujet .

63
Benjamin Gruenbaum

Je veux ajouter mes 2 cents ici. Considérant exactement la question "Créer une promesse es6 sans commencer à la résoudre" Je l'ai résolu en créant une fonction wrapper et en appelant la fonction wrapper à la place. Code:

Disons que nous avons une fonction f qui retourne une promesse

/** @return Promise<any> */
function f(args) {
   return new Promise(....)
}

// calling f()
f('hello', 42).then((response) => { ... })

Maintenant, je veux préparer un appel à f('hello', 42) sans le résoudre réellement:

const task = () => f('hello', 42) // not calling it actually

// later
task().then((response) => { ... })

J'espère que cela aidera quelqu'un :)


Référencer Promise.all() comme demandé dans les commentaires (et répondu par @Joe Frambach), si je veux préparer un appel à f1('super') & f2('Rainbow'), 2 fonctions qui renvoient des promesses

const f1 = args => new Promise( ... )
const f2 = args => new Promise( ... )

const tasks = [
  () => f1('super'),
  () => f2('Rainbow')
]

// later
Promise.all(tasks.map(t => t()))
  .then(resolvedValues => { ... })
38
Manu

Que diriez-vous d'une approche plus globale?

Vous pouvez écrire un constructeur qui retourne une nouvelle promesse décorée avec les méthodes .resolve() et .reject().

Vous choisiriez probablement de nommer le constructeur Deferred - un terme qui a beaucoup de préséance dans [l'historique des] promesses javascript.

function Deferred(fn) {
    fn = fn || function(){};

    var resolve_, reject_;

    var promise = new Promise(function(resolve, reject) {
        resolve_ = resolve;
        reject_ = reject;
        fn(resolve, reject);
    });

    promise.resolve = function(val) {
        (val === undefined) ? resolve_() : resolve_(val);
        return promise;//for chainability
    }
    promise.reject = function(reason) {
        (reason === undefined) ? reject_() : reject_(reason);
        return promise;//for chainability
    }
    promise.promise = function() {
        return promise.then(); //to derive an undecorated promise (expensive but simple).
    }

    return promise;
}

En retournant une promsie décorée plutôt qu'un simple objet, toutes les méthodes/propriétés naturelles de la promesse restent disponibles en plus des décorations.

De plus, en gérant fn, le modèle de révélateur reste disponible, si vous avez besoin/choisissez de l'utiliser sur un différé.

DÉMO

Maintenant, avec l'utilitaire Deferred() en place, votre code est pratiquement identique à l'exemple jQuery.

var deferreds = {};
function waitFor(key: string): Promise<any> {
  if (key in promises) {
    return deferreds[key].promise();
  }
  var def = Deferred();    
  deferreds[key] = def;
  return def.promise();
}
3
Roamer-1888