web-dev-qa-db-ja.com

非同期メソッド呼び出しの連鎖-JavaScript

2つの非同期メソッド呼び出し、barとbazを持つプロトタイプオブジェクトFooがあります。

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);
};

Bob.bar()。baz()を実行して、「bar」と「baz」を順にログに記録させます。

メソッドの呼び出しを変更できない場合(コールバック関数で渡すことを含む)、これらのメソッドの呼び出しにデフォルトのコールバックをどのように渡すことができますか?

いくつかのアイデア:

  1. 「ボブ」をデコレータでラップします(実装方法はまだあいまいですが、小さな例を使用できます)

  2. 何も割り当てられていない場合はデフォルトのコールバックを割り当てるようにコンストラクターを変更します(これが可能かどうかを考慮していません)。

  3. 何もなくなるまで次のメソッドを呼び出すジェネレーターラッパーを使用しますか?

14
Anthony Chung

代わりに、より推奨される方法は promises を使用することです。これは非同期のことをするコミュニティ全体の傾向なので。

Bob.bar()。baz()を実行して、「bar」と「baz」を順にログに記録させます。

このbob.bar().baz() "構文"を実現するためだけに、なぜそれを実行したいのですか?構文を機能させるための追加の努力なしでPromise APIを使用してそれをかなり簡単に行うことができる場合、実際にコードを複雑にし、実際のコードを理解することを困難にします。

そのため、次のようなプロミスベースのアプローチの使用を検討することをお勧めします。これは、アプローチによって達成したものよりもはるかに高い柔軟性を提供します。

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);
    };
};

これを実行して、次のように順次実行します。

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());

より多くの関数をチェーンする必要がある場合は、単純にそれを行うことができます。

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

とにかく、あなたがプロミスを使うことに慣れていないなら、あなたはあなたがしたいかもしれません これを読む

8
kabirbaidhya

警告これはまだ正しくありません。理想的には、Promiseをサブクラス化し、適切なthen/catch機能を備えていますが、サブクラス化 bluebird Promise にはいくつかの注意事項があります。アイデアは、Promise生成関数の内部配列を格納し、Promiseが待機するとき(その後/待機する)、それらのPromiseを順番に待機することです。

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')
})

結果:

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

コールバックの地獄を避け、正気を保ちたい場合は、関数型プログラミングのためにES6の約束が最も適切なアプローチです。同期タイムラインで作業するのと同じように、非同期タイムラインで順次非同期タスクをチェーンするだけです。

この特定のケースでは、非同期関数を約束する必要があるだけです。非同期関数がデータとasynch(data,myCallback)のようなコールバックを受け取ると仮定します。コールバックが最初のタイプのエラーであると仮定しましょう。

といった;

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

Asynch関数が約束された場合、実際には、データを受け取ってpromiseを返す関数が返されます。 myCallbackの段階でthenを適用する必要があります。次に、myCallbackの戻り値が次のステージに渡されます。このステージでは、myCallbackの戻り値が指定された別の非同期関数を呼び出すことができます。これは、必要な限り繰り返されます。それでは、ワークフローにこの抽象を実装する方法を見てみましょう。

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)
1
Redu