IT Share you

Javascript에서 비동기 / 대기 병렬 실행 방법

shareyou 2021. 1. 6. 08:11
반응형

Javascript에서 비동기 / 대기 병렬 실행 방법


마지막으로 async/ await됩니다 지원 IE를 제외한 모든 주요 브라우저 조만간에. 이제 async/를 사용 하여 더 읽기 쉬운 코드를 작성할 수 await있지만 문제가 있습니다. 많은 사람들이 다음과 같이 비동기를 사용합니다.

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

다음과 같이 작성할 수 있습니다.

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

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

이건 쉬워요? 그러나 문제가 있습니다. Promise.all르파 행동 수단, 그것은 곧 약속 중 하나가 거부로 거부합니다. 아마도 당신은 우리가 가져 오기 거부를 처리하는 더 강력한 솔루션을 원할 것입니다. 다행히 그것은 간단하게 달성 할 수있는 솔루션이 async/ await사용의 필요없이 Promise.all. 실제 예 :

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/ await 활성화 된 브라우저가 필요합니다.

이렇게하면 단순히 try/ catch를 사용하여 오류를 처리하고 parallel함수 내에서 부분적인 결과를 반환 할 수 있습니다 .


Promise.all의 fail-fast 동작과 비 구조화 할당 구문에 괜찮다면 :

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

이를 런타임으로 결정된 호출 수로 확장하는 방법을 묻는 사람들은 2 개의 루프를 사용할 수 있습니다. 첫 번째는 모든 작업을 시작하고 두 번째는 모든 작업이 완료 될 때까지 기다립니다.

console.clear();

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

/** 
 * While here we call the functions first,
 * then wait for the result later, so 
 * this will finish in 500ms.
 */
async function runTasks(timings) {
  let tasks = [];
  for (let i in timings) {
      tasks.push(wait(timings[i], `Result of task ${i}`));
  }

  /* Want fast fail? use Promise.All */
  //return Promise.All(tasks);
  
  let results = [];
  for (let task of tasks) {
       results.push(await task);
  }

  return results;
}

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

void taskRunner(runTasks, [50,100,200,60,500], 'Task List');


나는 실제로 똑같은 일을했습니다. promise를 사용한 다음 Promise.all을 사용하여 마지막에 동기화하면 많은 동시 요청을 수행 할 수 있지만 완료하기 전에 모든 결과가 다시 있는지 확인하십시오.

마지막 예에서 http://javascriptrambling.blogspot.com/2017/04/to-promised-land-with-asyncawait-and.html을 참조하십시오.


의사 코드는 다음과 같이 작성할 수 있습니다.

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 will run in parallel. combinedResult and lastResult will also run in parallel. However combinedResult value i.e. return of handleResults function will be returned once the result-1 and result-2 are available and lastResult value i.e handleLastResult will be returned once the result-n is available.

Hope this helps


First, are your code a blocking-code?

If yes, remember that javascript is single thread, so you cannot run two synchronous codes, for example two loops (for or while) at the same time.

But, it is possible to achieve that using Web Workers, I managed to execute functions in generic web workers and without using separated js files.

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

ReferenceURL : https://stackoverflow.com/questions/42158853/how-run-async-await-in-parallel-in-javascript

반응형