2017-02-10 15 views
19

最後にasync/awaitは、すぐにIEを除くすべての主要ブラウザでsupportedになります。 これで、async/awaitのコードを書くことができますが、キャッチがあります。多くの人々は、非同期のように待って使用します。Javascriptで非同期/待機を並行して実行する方法

const userResponse = await fetchUserAsync(); 
const postsResponse = await fetchPostsAsync(); 

このコードは、それが問題を抱えている読みやすいですが、それはユーザーの取り込みが終了するまで、それは記事をフェッチ開始されません、シリーズ内の関数を実行します。ソリューションは単純ですが、リソースを並行して取得する必要があります。

だから、私は何をしたい(疑似言語で)です:

fn task() { 
    result-1 = doAsync(); 
    result-2 = doAsync(); 
    result-n = doLongAsync(); 

    // handle results together 
    combinedResult = handleResults(result-1, result-2); 

    lastResult = handleLastResult(result-n); 
} 

答えて

43

あなたはこのような何か書くことができます。これは簡単に右である

const responses = await Promise.all([ 
fetchUserAsync(), 
fetchPostsAsync(), 
]); 

const userResponse = responses[0]; 
const postsResponse = responses[1]; 

を?しかし、キャッチがあります。 Promise.allfail-fastの動作を持っています。つまり、約束が拒否されるとすぐに拒否します。おそらく、あなたはフェッチのいずれかの拒否を処理することを担当するより堅牢なソリューションを望んでいるでしょう。幸運にも解決策があります。Promise.allを使わずにasync/awaitで簡単に達成できます。作業例:

console.clear(); 
 

 
function wait(ms, data) { 
 
    return new Promise(resolve => setTimeout(resolve.bind(this, data), ms)); 
 
} 
 

 
/** 
 
* This will run in series, because 
 
* we call a function and immediately wait for it's result, 
 
* so this will finish in 1s. 
 
*/ 
 
async function series() { 
 
    return { 
 
    result1: await wait(500, 'seriesTask1'), 
 
    result2: await wait(500, 'seriesTask2'), 
 
    } 
 
} 
 

 
/** 
 
* While here we call the functions first, 
 
* then wait for the result later, so 
 
* this will finish in 500ms. 
 
*/ 
 
async function parallel() { 
 
    const task1 = wait(500, 'parallelTask1'); 
 
    const task2 = wait(500, 'parallelTask2'); 
 

 
    return { 
 
    result1: await task1, 
 
    result2: await task2, 
 
    } 
 
} 
 

 
async function taskRunner(fn, label) { 
 
    const startTime = performance.now(); 
 
    console.log(`Task ${label} starting...`); 
 
    let result = await fn(); 
 
    console.log(`Task ${label} finished in ${ Number.parseInt(performance.now() - startTime) } miliseconds with,`, result); 
 
} 
 

 
void taskRunner(series, 'series'); 
 
void taskRunner(parallel, 'parallel'); 
 

 

 
/* 
 
* The result will be: 
 
* Task series starting... 
 
* Task parallel starting... 
 
* Task parallel finished in 500 milliseconds with, { "result1": "parallelTask1", "result2": "parallelTask2" } 
 
* Task series finished in 1001 milliseconds with, { "result1": "seriesTask1", "result2": "seriesTask2" } 
 
*/

注:あなたが(またはそれ以上nodejsのV7と)このスニペットを実行するためにasync/awaitenabledを持っているブラウザが必要になります

あなたが使用することができます。この方法単にtry/catchと入力してエラーを処理し、parallel関数内で部分的な結果を返します。

+2

コードの素敵な一枚!非常に役に立ちました –

+0

あなたのコードについていくつか質問があります。どのようにあなたは並列プールを待っていますか?あなたの例では、実行して結果を返す2つのタスクがあることを知っています。どのようにしてforループを使用して作成するのですか(書き込みスクリプトの瞬間にタスク数を知らないと言うことができます)。私のユースケース:HTTPコールからいくつかのIDを取得し、各IDに対して実行するタスクがあります。検索されたIDのすべてのタスクを並行して実行するにはどうすればよいですか? – BlackHoleGalaxy

+0

ありがとう、良い質問!私は自分のMacの前にいるときに私の答えを更新します。 – NoNameProvided

0

私は実際にこの同じことをしました。プロミスを使用してからPromise.allを使用して最後に同期させることで、多くの同時リクエストを実行できますが、完了する前にすべての結果が戻っていることを確認してください。

最後の例では、ここを参照してください: http://javascriptrambling.blogspot.com/2017/04/to-promised-land-with-asyncawait-and.html

+0

私が言ったように、Promise.allは高速に動作しないので、同じ目標をアーカイブしていません。 – NoNameProvided

+0

ああ、あなたがあなた自身の質問をして、あなたがそれに答えることができたことを理解していませんでした。しかし、元の質問では、すべてのエラーを処理したいとは言いませんでした。通常、1つのエラーだけを処理するだけで十分です。これは、1つのパートでも障害が発生してもプロセスが成功することは稀です。それが必要な場合を除き、コードが少なくて済みます。 –

9

あなたがPromise.allのフェイルファストの動作および分割代入構文でOKなら:

const [userResponse, postsResponse] = await Promise.all([ 
    fetchUserAsync(), 
    fetchPostsAsync(), 
]); 
0

擬似コードをすることができ

fn async task() { 
    result-1 = doAsync(); 
    result-2 = doAsync(); 
    result-n = doLongAsync(); 
    try{ 
    // handle results together 
    combinedResult = handleResults(await result-1, await result-2); 
    lastResult = handleLastResult(await result-n); 
    } 
    catch(err){ 
    console.error(err) 
    } 

} 

result-1、result-2、result-nは並行して実行されます。combinedResultとlastResultも同時に実行されます。 result-1とresult-2が利用可能になるとcombinedResultの戻り値、つまりhandleResults関数が返され、result-nが利用可能になると、lastResult値、つまりhandleLastResultが返されます。

希望これは

+0

このリンクを理解するには、https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_functionを参照してください。 –

0

まず助け、あなたのコードは、ブロッキング・コードですか?

もしそうであれば、javascriptはシングルスレッドなので、2つの同期コード(例えばforまたはwhile)を同時に実行することはできません。

しかし、Webワーカーを使用すると、ジェネリックWebワーカーで機能を実行し、別々のjsファイルを使用せずに達成することが可能です。そこ

setInterval(()=>{console.log("non blocked " + Math.random())}, 900) 
 

 
console.log("start blocking code in parallel in web Worker") 
 
console.time("blocked") 
 

 
genericWorker(window, ["blockCpu", function (block){  
 
    block(10000) //This blockCpu function is defined below 
 
    return "\n\nbla bla\n" //This is catched in the resolved promise 
 

 
}]).then(function (result){ 
 
    console.timeEnd("blocked") 
 
    console.log("End of blocking code", result) 
 
}) 
 
.catch(function(error) { console.log(error) }) 
 

 

 
/* A Web Worker that does not use a File, it create that from a Blob 
 
    @cb_context, The context where the callback functions arguments are, ex: window 
 
    @cb, ["fn_name1", "fn_name2", function (fn1, fn2) {}] 
 
     The callback will be executed, and you can pass other functions to that cb 
 
*/ 
 
function genericWorker(cb_context, cb) { 
 
    return new Promise(function (resolve, reject) { 
 

 
     if (!cb || !Array.isArray(cb)) 
 
      return reject("Invalid data") 
 

 
     var callback = cb.pop() 
 
     var functions = cb 
 

 
     if (typeof callback != "function" || functions.some((fn)=>{return typeof cb_context[fn] != "function"})) 
 
      return reject(`The callback or some of the parameters: (${functions.toString()}) are not functions`) 
 

 
     if (functions.length>0 && !cb_context) 
 
      return reject("context is undefined") 
 

 
     callback = fn_string(callback) //Callback to be executed 
 
     functions = functions.map((fn_name)=> { return fn_string(cb_context[fn_name]) }) 
 

 
     var worker_file = window.URL.createObjectURL(new Blob(["self.addEventListener('message', function(e) { var bb = {}; var args = []; for (fn of e.data.functions) { bb[fn.name] = new Function(fn.args, fn.body); args.push(fn.name)}; var callback = new Function(e.data.callback.args, e.data.callback.body); args = args.map(function(fn_name) { return bb[fn_name] }); var result = callback.apply(null, args) ;self.postMessage(result);}, false)"])) 
 
     var worker = new Worker(worker_file) 
 

 
     worker.postMessage({ callback: callback, functions: functions }) 
 

 
     worker.addEventListener('error', function(error){ return reject(error.message) }) 
 

 
     worker.addEventListener('message', function(e) { 
 
      resolve(e.data), worker.terminate() 
 
     }, false) 
 

 
     //From function to string, with its name, arguments and its body 
 
     function fn_string (fn) { 
 
      var name = fn.name, fn = fn.toString() 
 

 
      return { name: name, 
 
       args: fn.substring(fn.indexOf("(") + 1, fn.indexOf(")")), 
 
       body: fn.substring(fn.indexOf("{") + 1, fn.lastIndexOf("}")) 
 
      } 
 
     } 
 
    }) 
 
} 
 

 
//random blocking function 
 
function blockCpu(ms) { 
 
    var now = new Date().getTime(), result = 0 
 
    while(true) { 
 
     result += Math.random() * Math.random(); 
 
     if (new Date().getTime() > now +ms) 
 
      return; 
 
    } 
 
}

関連する問題