web-dev-qa-db-fra.com

chaînage d'appels de méthode async - javascript

Vous avez un objet prototype Foo avec deux appels de méthode async, bar et baz.

var bob = new Foo()

Foo.prototype.bar = function land(callback) {
  setTimeout(function() {
    callback()
    console.log('bar');
  }, 3000);
};

Foo.prototype.baz = function land(callback) {
  setTimeout(function() {
    callback()
    console.log('baz');
  }, 3000);
};

Nous voulons faire bob.bar (). Baz () et le consigner "bar" et "baz" séquentiellement.

Si vous ne pouvez pas modifier les appels de méthode (y compris la transmission de votre fonction de rappel), comment pouvez-vous passer un rappel par défaut à ces appels de méthode? 

Quelques idées:

  1. Enveloppez "bob" avec le décorateur (toujours confus sur la manière de la mettre en œuvre, vous pouvez utiliser un petit exemple)

  2. Modifier le constructeur pour assigner un rappel par défaut si aucun assigné (n'a pas considéré si cela est possible ou non)

  3. Utilisez un générateur de générateur qui continuera à appeler la méthode suivante jusqu'à ce qu'il n'en reste plus?

13
Anthony Chung

La méthode la plus recommandée consiste à utiliser promises . Comme il s'agit d'une tendance à l'échelle de la communauté à faire des choses asynchrones.

Nous voulons faire bob.bar (). Baz () et le connecter "bar" et "baz" séquentiellement.

Pourquoi voudriez-vous faire cela juste pour réaliser cette "syntaxe" bob.bar().baz()? Lorsque vous pouviez le faire assez simplement en utilisant l'API Promise sans efforts supplémentaires pour que cette syntaxe fonctionne, cela augmente effectivement la complexité du code, ce qui rend le code difficile à comprendre.

Donc, vous voudrez peut-être envisager d'utiliser l'approche basée sur les promesses un peu comme ceci, qui offre beaucoup plus de flexibilité que ce que vous auriez réalisé avec votre approche:

Foo.prototype.bar = function () {
    return new Promise(function (resolve) {
        setTimeout(function () {
            resolve()
            console.log('bar');
        }, 3000);
    };
};

Foo.prototype.baz = function () {
    return new Promise(function (resolve) {
        setTimeout(function () {
            resolve()
            console.log('baz');
        }, 3000);
    };
};

Maintenant, vous feriez ceci pour les exécuter séquentiellement l'un après l'autre:

var bob = new Foo();

bob.bar().then(function() {
   return bob.baz();
});

// If you're using ES2015+ you could even do:
bob.bar().then(() => bob.baz());

Si vous avez besoin de chaîner plus de fonctions, vous pouvez simplement le faire:

bob.bar()
    .then(() => bob.baz())
    .then(() => bob.anotherBaz())
    .then(() => bob.somethingElse());  

Quoi qu’il en soit, si vous n’êtes pas habitué à utiliser des promesses, vous voudrez peut-être lisez ceci

9
kabirbaidhya

Attention Ce n'est pas encore tout à fait ça. Idéalement, nous sous-classerions Promise et disposerions des fonctionnalités correctes de la commande then/catch, mais il y a quelques réserves avec le sous-classement bluebird Promise . L'idée est de stocker un tableau interne de fonctions générant des promesses, puis, lorsqu'une promesse est attendue (ensuite/attend), attendre sérieusement ces promesses.

const Promise = require('bluebird');

class Foo {
  constructor() {
    this.queue = [];
  }

  // promise generating function simply returns called pGen
  pFunc(i,pGen) {
    return pGen();
  }

  bar() {
    const _bar = () => {
      return new Promise( (resolve,reject) => {
        setTimeout( () => {
          console.log('bar',Date.now());
          resolve();
        },Math.random()*1000);
      })      
    }
    this.queue.Push(_bar);
    return this;
  }

  baz() {
    const _baz = () => {
      return new Promise( (resolve,reject) => {
        setTimeout( () => {
          console.log('baz',Date.now());
          resolve();
        },Math.random()*1000);
      })      
    }
    this.queue.Push(_baz);
    return this;
  }

  then(func) {
    return Promise.reduce(this.queue, this.pFunc, 0).then(func);
  }
}


const foo = new Foo();
foo.bar().baz().then( () => {
  console.log('done')
})

résultat:

messel@messels-MBP:~/Desktop/Dropbox/code/js/async-chain$ node index.js 
bar 1492082650917
baz 1492082651511
done
3
Mark Essel

Si vous voulez éviter les rappels et garder votre santé mentale, les promesses de l'ES6 sont l'approche la plus appropriée pour la programmation fonctionnelle. Vous venez de chaîner vos tâches asynchrones séquentielles dans la chronologie asynchrone, tout comme si vous travailliez dans une chronologie synchrone.

Dans ce cas particulier, il vous suffit de promettre vos fonctions asynchrones. Supposons que vos fonctions asynchrones prennent des données et un rappel comme asynch(data,myCallback). Supposons que le rappel est le premier type d'erreur.

Tel que;

var myCallback = (error,result) => error ? doErrorAction(error)
                                         : doNormalAction(result)

Lorsque votre fonction asynchrone est promise, vous recevrez en réalité une fonction qui prend vos données et renvoie une promesse. Vous devez appliquer myCallback au stade then. La valeur de retour de myCallback sera ensuite passée à l'étape suivante à laquelle vous pourrez appeler une autre fonction asynchrone fournie avec la valeur de retour de myCallback et ce, tant que vous en aurez besoin. Voyons donc comment nous allons implémenter ce résumé dans votre flux de travail.

function Foo(){}

function promisify(fun){
  return (data) => new Promise((resolve,reject) => fun(data, (err,res) => err ? reject(err) : resolve(res)));
}

function myCallback(val) {
  console.log("hey..! i've got this:",val);
  return val;
}

var bob = new Foo();

Foo.prototype.bar = function land(value, callback) {
  setTimeout(function() {
    callback(false,value*2);  // no error returned but value doubled and supplied to callback
    console.log('bar');
  }, 1000);
};

Foo.prototype.baz = function land(value, callback) {
  setTimeout(function() {
    callback(false,value*2);  // no error returned but value doubled and supplied to callback
    console.log('baz');
  }, 1000);
};

Foo.prototype.bar = promisify(Foo.prototype.bar);
Foo.prototype.baz = promisify(Foo.prototype.baz);

bob.bar(1)
   .then(myCallback)
   .then(bob.baz)
   .then(myCallback)

0
Redu