2017-12-07 15 views
0

私は約束事の問題を抱えています。私は処理してmongoデータベースに取り込む必要があるデータを返す外部APIを呼び出します。私はエクスプレスでnodejsとmongodbを使用しています。とにかく、APIへの呼び出しが正常に動作している、問題は私が一度にそれらのトンを作っているということです。私はそれらを遅くしたい、一組のすべての呼び出しをするように。ちょっと待って。次のセットのすべての呼び出しを行います。これが既知の量のセットだったら、私はそれらを連鎖することを約束します。どれくらいのセットがあるかわからないので、私はそれらをループしています。私は閉鎖が問題だと思うが、それを回避することはできない。上の例のコードに!nodejsの約束事の配列に遅延やスリープを追加する

function readApi(carFactory){ 
    var promise = new Promise(function(resolve, reject) { 
     // call out to api, get set of car data from factory1 

     console.log(carFactory); 
     if (true) { 
     console.log('resolved'); 
     resolve("Stuff worked!"+carFactory); 
     } 
     else { 
     reject(Error("It broke")); 
     } 
    }); 
    return promise; 
    } 

    function manager(){ 

    //singular call 
    readApi("this is a singular test").then(returnedThing=>{ 
     console.log(returnedThing); //Stuff worked! this is a singular test 
    }); 

    let dynamicList = ["carFactory1", "carFactory2","carFactory3","carFactory..N"]; 
    let readApiAction = []; 
    dynamicList.forEach(carIter=>{ 
     readApiAction.push(readApi(carIter)); 
    }); 
    //ok so now I got an array of promise objects. 
    //I want to call the first one, wait 1 minute and then call the next one. 

    //originally I was calling promise.all, but there is no way to get at 
    //each promise to pause them out so this code is what I am looking to fix 
    let results= Promise.all(readApiAction); 
    results.then(data=>{ 
     data.forEach(resolvedData=>{ 
     console.log(resolvedData); //Stuff worked carFactory1, etc... 
     });  
    }); 


    //singular call with timeout, this does not work, each one called at the same time 
    let readApiActionTimeouts = []; 
    dynamicList.forEach(carIter=>{ 
     setTimeout(callingTimeout(carIter), 6000); 
    }); 
    } 

    //just a function to call the promise in a timeout 
    //this fails with this - TypeError: "callback" argument must be a function 
    function callingTimeout(carIter){ 
    readApi(carIter).then(returnedThing=>{ 
     console.log("timeout version"+returnedThing); 
    }); 
    } 
+0

です。 – jfriend00

+2

関連の回答:[)(Promise.allの各約束を遅らせる](https://stackoverflow.com/questions/47383610/nodejs-delay-each-promise-within-promise-all/47394678#47394678)、[どのように制御します一度に飛行中の多くのリクエスト](https://stackoverflow.com/questions/47299174/nodejs-async-request-with-a-list-of-url/47299802#47299802)、[できるようにする方法javascriptで一度に10の約束を実行して、api呼び出しのレート制限を防止しますか?](https://stackoverflow.com/questions/44666202/how-to-make-it-so-that-i-canecute-say -10-promises-at-a-time-in-javascript-to/44666278#44666278)。 – jfriend00

+0

以上の関連回答:[バッハ処理のための適切な方法を選択し(https://stackoverflow.com/questions/36730745/choose-proper-async-method-for-batch-processing/36736593#36736593)、[における約束間の遅延約束チェーン](https://stackoverflow.com/questions/41079410/delays-between-promises-in-promise-chain/41079572#41079572)、[遅延が約束をチェーン](https://stackoverflow.com/questions/38734106/delay-chained-promise/38734304#38734304)。 – jfriend00

答えて

1

ビットを、これはそれをFIXEする方法です。ネイティブPromise.allグループの約束だけ。それらはまだ(非同期の方法で、すべてのJSコードとして、しかしお互いに)同時に実行されます。これは、まだAPIを混雑させ、多くの呼び出しを実行することを意味します。

約束を延期したい場合は、戻り値を遅らせる必要があります(例:resolve)。これを行うには、setTimeout INSIDE新しいPromiseを使用することができます(詳細は下をご覧ください)。

タイムアウトの設定は非同期です。他の言語と同じように機能しません(コード全体を一時停止するだけではありません)。あなたのコードで固定タイムアウトを設定すると、すべての実行を6秒だけ移動させました。彼らはまだ並行して起こった(しかし、異なるダニで、しかしそれは小さな違いです)。試してみてくださいループ内のそれぞれに異なるタイムアウトを生成します - あなたはそれらが異なる時間に起こっているのに気づくでしょうが!これはpromisifiedコードのための良い解決策ではありません!

実用的な答えを今すぐ!

Bluebirdを使用する場合は、遅延やタイムアウトを各約束に追加する特別な方法があります。それがなければ、Promiseの周りにラッパーを書く必要があります。特定の時間が経過したら解決してPromise.allと一緒に使用してください。

まず溶液(ブルーバード):

function delayMyPromise(myPromise, myDelay); 
    return Promise.delay(myDelay).then(function() { 
    return myPromise; 
    }); 
}); 

、その後、あなたのコード内で:

return Promise.all(delayMyPromise(promise1, 1000), delayMyPromise(promise2, 2000)); // Notice different delays, you may generate them programatically 

あるいはクーラー、あなたが代わりに特殊な同時性の設定をそれほど持っていPromise.allのブルーバードからPromise.mapを使用することができますあなたは、あなたの約束が特定の順序で実行されるように強制することができます。 2つずつ。

ピュアネイティブの約束の実現:

function delayMyPromise(myPromise, myDelay); 
    return new Promise(function (resolve, reject) { 
    setTimeout(function() { 
     return resolve(myPromise); 
    }, myDelay); 
    }); 
}); 

I私は私の以前のプロジェクト:)ここ

もっと上でそれをやった ですしかし、大いにお勧めしますあなたがブルーバードの使用に気をつけなければ、最初のアプローチです。外部サーバへのAPI呼び出しの多くを行う際にレート制限をどのように扱うかについてのSO上で同様のタイプの質問の数十があります。それは約束のためにlodashようなものだし、それは本当に速い:)

+0

この回答に感謝します。私はこれを行って、あなたに戻ってきます – spartikus

+0

別のライブラリを追加する必要はありません、もう一度おかげさま! – spartikus

1

このような場合に再帰を使用できます。

.forEachに電話すると、すぐに各繰り返しが発生します。

以下の例では、setTimeoutが発生するまでdoSomethingは呼び出されません。つまり、各文字は1秒間隔で印刷されます。

let letters = ["a", "b", "c"]; 
 

 
function doSomething(arr) { 
 
    console.log(arr[0]); 
 
    if (arr.length > 1) { 
 
    setTimeout(() => doSomething(arr.slice(1)), 1000); 
 
    } 
 
} 
 

 
doSomething(letters);

代わりに、約束のあなたの配列のために:あなたのcallingTimeout返し何とsetTimeoutは、機能を必要としないので、TypeError: "callback" argument must be a function

let promises = [ 
 
    Promise.resolve("A"), 
 
    Promise.resolve("B"), 
 
    Promise.resolve("C"), 
 
]; 
 

 
function doSomething(arr) { 
 
    arr[0].then((result) => { 
 
    console.log(result); 
 
    if (arr.length > 1) { 
 
     setTimeout(() => doSomething(arr.slice(1)), 1000); 
 
    } 
 
    }) 
 
} 
 

 
doSomething(promises);

+0

文字が約束の配列だった場合、これはうまくいきますか?そして、約束の中のどこが呼び出されるのでしょうか? – spartikus

+1

私は – user184994

1

エラーが発生します引数として

let readApiActionTimeouts = []; 
    dynamicList.forEach(carIter=>{ 
      callingTimeout(carIter) 
    }); 

あなたの約束:理論上の

function readApi(carFactory){ 
    var promise = new Promise(function(resolve, reject) { 
     //... 
     setTimeout(()=>{ 
      resolve("Stuff worked!"+carFactory); 
     }, 6000); 
     //... 
    }); 
    return promise; 
    } 
+0

の例で更新しました。私のリストに1つだけあれば、1分待ってから解決しますか? – spartikus

+0

はいいいえ –

+1

@spartikusリストに1つまたは多くがあるかどうかを知るために新しい変数を追加することができます –

関連する問題