javascript - समानांतर में async/प्रतीक्षा कार्यों को कॉल करें




node.js asynchronous (6)

जहां तक ​​मैं समझता हूं, ES7 / ES2016 में कई await कोड डालने से वादों के साथ .then() समान काम होगा, जिसका अर्थ है कि वे लंबन के बजाय एक के बाद एक निष्पादित करेंगे। इसलिए, उदाहरण के लिए, हमारे पास यह कोड है:

await someCall();
await anotherCall();

क्या मैं इसे सही तरीके से समझता हूं कि एक और someCall() पूरा होने पर ही कॉल किया जाएगा? उन्हें समानांतर में कॉल करने का सबसे सुरुचिपूर्ण तरीका क्या है?

मैं इसे नोड में उपयोग करना चाहता हूं, इसलिए हो सकता है कि async लाइब्रेरी के साथ कोई समाधान हो?

संपादित करें: मैं इस प्रश्न में प्रदान किए गए समाधान से संतुष्ट नहीं हूं: एसिंक्स जनरेटर में वादों के गैर-समानांतर इंतजार की वजह से मंदी , क्योंकि यह जनरेटर का उपयोग करता है और मैं अधिक सामान्य उपयोग के मामले के बारे में पूछ रहा हूं।


सहज समाधान

function wait(ms, data) {
  console.log('Starting task:', data, ms);
  return new Promise( resolve => setTimeout(resolve, ms, data) );
}

(async function parallel() {

  // step 1 - initiate all promises
  console.log('STARTING')
  let task1 = wait(2000, 'parallelTask1') // PS: see Exception handling below
  let task2 = wait(500, 'parallelTask2')
  let task3 = wait(1000, 'parallelTask3')

  // step 2 - await all promises
  console.log('WAITING')
  task1 = await task1
  task2 = await task2
  task3 = await task3

  // step 3 - all results are 100% ready
  console.log('FINISHED')
  console.log('Result:', task1, task2, task3)

})()

  1. यह एक-एक करके वादों पर अमल करेगा, लेकिन तुरन्त और वे साथ-साथ चलते रहेंगे।
  2. यह वह जगह है जहां हम आगे कोड निष्पादन को रोकते हैं और उनके समाप्त होने की प्रतीक्षा करते हैं। यह आदेश पर कोई फर्क नहीं पड़ता और जो पहले हल करता है। कोड सभी के समाधान से पहले चरण 3 तक जारी नहीं रहेगा। यदि पहला सबसे लंबा होता है, तो उसे दूसरे के लिए अधिक इंतजार नहीं करना होगा क्योंकि यह उस समय तक पूरा हो जाएगा जब कोड वहां होगा।
  3. यह किया जाता है, वादों में से अंतिम हल हो गया है और कोड निष्पादन अंतिम await कॉल पूरा हुआ।

ES6 के साथ, आप चरण 2 में भी कर सकते हैं, क्रियान्वित दीक्षा के बाद

[task1, task2, task3] = [await task1, await task2, await task3]

पुनश्च: आप गणना के अंदर भी इंतजार कर सकते हैं

let five = getAsyncFive()
let ten = getAsyncTen()

let result = await five * await ten

* ध्यान दें कि यह let result = await getAsyncFive() * await getAsyncTen() के समान नहीं है let result = await getAsyncFive() * await getAsyncTen() करें क्योंकि यह समानांतर में async कार्य नहीं चलाएगा!

उपवाद सम्भालना

नीचे दिए गए स्निपेट में .catch(e => e) एक त्रुटि पकड़ता है और चेन को जारी रखने की अनुमति देता है, वादे को अस्वीकार करने के बजाय हल करने की अनुमति देता है। catch बिना, कोड एक अनजाने अपवाद को फेंक देगा और फ़ंक्शन जल्दी बाहर निकल जाएगा।

const wait = (ms, data) => log(ms,data) || new Promise( resolve => setTimeout(resolve, ms, data) )
const reject = (ms, data) => log(ms,data) || new Promise( (r, reject) => setTimeout(reject, ms, data) )
const e = e => 'err-' + e
const l = l => (console.log('Done:', l), l)
const log = (ms, data) => console.log('Started', data, ms)

;(async function parallel() {

  let task1 = reject(500, 'parallelTask1').catch(e).then(l)
  let task2 = wait(2000, 'parallelTask2').catch(e).then(l)
  let task3 = reject(1000, 'parallelTask3').catch(e).then(l)
  
  console.log('WAITING')

  task1 = await task1
  task2 = await task2
  task3 = await task3

  console.log('FINISHED', task1, task2, task3)

})()

दूसरा स्निपेट संभाला नहीं जाता है और फ़ंक्शन विफल हो जाएगा।
आप Devtools भी खोल सकते हैं और कंसोल आउटपुट में त्रुटियों को देख सकते हैं।

const wait = (ms, data) => log(ms,data) || new Promise( resolve => setTimeout(resolve, ms, data) )
const reject = (ms, data) => log(ms,data) || new Promise( (r, reject) => setTimeout(reject, ms, data) )
const e = e => 'err-' + e
const l = l => (console.log('Done:', l), l)
const log = (ms, data) => console.log('Started', data, ms)

console.log('here1')

;(async function parallel() {

  let task1 = reject(500, 'parallelTask1').then(l) // catch is removed
  let task2 = wait(2000, 'parallelTask2').then(l)
  let task3 = reject(1000, 'parallelTask3').then(l)
  
  console.log('WAITING')

  task1 = await task1
  task2 = await task2
  task3 = await task3

  console.log('FINISHED', task1, task2, task3)

})()

console.log('here2') // Note: "FINISHED" will not run


Promise.all के बिना एक और तरीका है () इसे समानांतर में करने के लिए:

सबसे पहले, हम संख्याओं को प्रिंट करने के लिए 2 कार्य करते हैं:

function printNumber1() {
   return new Promise((resolve,reject) => {
      setTimeout(() => {
      console.log("Number1 is done");
      resolve(10);
      },1000);
   });
}

function printNumber2() {
   return new Promise((resolve,reject) => {
      setTimeout(() => {
      console.log("Number2 is done");
      resolve(20);
      },500);
   });
}

यह अनुक्रमिक है:

async function oneByOne() {
   const number1 = await printNumber1();
   const number2 = await printNumber2();
} 
//Output: Number1 is done, Number2 is done

यह समानांतर है:

async function inParallel() {
   const promise1 = printNumber1();
   const promise2 = printNumber2();
   const number1 = await promise1;
   const number2 = await promise2;
}
//Output: Number2 is done, Number1 is done

मैं एक सहायक फ़ंक्शन वेट बनाता हूं, हो सकता है कि यह इसे मीठा बना सकता है। यह केवल अब के लिए नोडज में काम करता है, ब्राउज़र क्रोम में नहीं

    //const parallel = async (...items) => {
    const waitAll = async (...items) => {
        //this function does start execution the functions
        //the execution has been started before running this code here
        //instead it collects of the result of execution of the functions

        const temp = [];
        for (const item of items) {
            //this is not
            //temp.push(await item())
            //it does wait for the result in series (not in parallel), but
            //it doesn't affect the parallel execution of those functions
            //because they haven started earlier
            temp.push(await item);
        }
        return temp;
    };

    //the async functions are executed in parallel before passed
    //in the waitAll function

    //const finalResult = await waitAll(someResult(), anotherResult());
    //const finalResult = await parallel(someResult(), anotherResult());
    //or
    const [result1, result2] = await waitAll(someResult(), anotherResult());
    //const [result1, result2] = await parallel(someResult(), anotherResult());

मैं वोट देता हूं:

await Promise.all([someCall(), anotherCall()]);

ऐसे क्षणों से अवगत रहें जिन्हें आप कार्य कहते हैं, यह अप्रत्याशित परिणाम का कारण हो सकता है:

// Supposing anotherCall() will trigger a request to create a new User

if (callFirst) {
  await someCall();
} else {
  await Promise.all([someCall(), anotherCall()]); // --> create new User here
}

लेकिन नए उपयोगकर्ता बनाने के लिए हमेशा निम्नलिखित ट्रिगर अनुरोध करते हैं

// Supposing anotherCall() will trigger a request to create a new User

const someResult = someCall();
const anotherResult = anotherCall(); // ->> This always creates new User

if (callFirst) {
  await someCall();
} else {
  const finalResult = [await someResult, await anotherResult]
}

अपडेट करें:

मूल उत्तर यह वादा अस्वीकार को सही ढंग से संभालने के लिए (और कुछ मामलों में असंभव) मुश्किल बनाता है। Promise.all का उपयोग करने के लिए सही समाधान है:

const [someResult, anotherResult] = await Promise.all([someCall(), anotherCall()]);

मूल उत्तर:

इससे पहले कि आप या तो एक का इंतजार कर लें, बस सुनिश्चित करें कि आप दोनों कार्यों को कॉल करते हैं:

// Call both functions
const somePromise = someCall();
const anotherPromise = anotherCall();

// Await both promises    
const someResult = await somePromise;
const anotherResult = await anotherPromise;

टी एल; डॉ

समानांतर फ़ंक्शन कॉल के लिए Promise.all उपयोग करें, उत्तर व्यवहार ठीक से तब होता है जब त्रुटि होती है।

सबसे पहले, एक बार में सभी अतुल्यकालिक कॉल निष्पादित करें और सभी Promise प्राप्त करें। दूसरा, Promise ऑब्जेक्ट्स पर Promise उपयोग करें। इस तरह, जबकि आप दूसरे अतुल्यकालिक कॉल को हल करने के लिए पहले Promise प्रतीक्षा करते हैं, अभी भी प्रगति कर रहे हैं। कुल मिलाकर, आप केवल सबसे धीमी एसिंक्रोनस कॉल के रूप में लंबे समय तक इंतजार करेंगे। उदाहरण के लिए:

// Begin first call and store promise without waiting
const someResult = someCall();

// Begin second call and store promise without waiting
const anotherResult = anotherCall();

// Now we await for both results, whose async processes have already been started
const finalResult = [await someResult, await anotherResult];

// At this point all calls have been resolved
// Now when accessing someResult| anotherResult,
// you will have a value instead of a promise

JSbin उदाहरण: http://jsbin.com/xerifanima/edit?js,console

कैविएट: यह कोई फर्क नहीं पड़ता कि await कॉल एक ही लाइन पर या विभिन्न लाइनों पर हैं, इसलिए जब तक कि एसिंक्रोनस कॉल के बाद पहली await कॉल होती है। देखें जॉनीएचके की टिप्पणी

अपडेट: इस उत्तर में @ बर्गी के उत्तर के अनुसार त्रुटि से निपटने में एक अलग समय है, यह त्रुटि को बाहर नहीं फेंकता है क्योंकि त्रुटि होती है लेकिन सभी वादों के निष्पादन के बाद। मैं परिणाम की तुलना @ [result1, result2] = Promise.all([async1(), async2()]) टिप से करता [result1, result2] = Promise.all([async1(), async2()]) : [result1, result2] = Promise.all([async1(), async2()]) , निम्न कोड स्निपेट की जांच करें

const correctAsync500ms = () => {
  return new Promise(resolve => {
    setTimeout(resolve, 500, 'correct500msResult');
  });
};

const correctAsync100ms = () => {
  return new Promise(resolve => {
    setTimeout(resolve, 100, 'correct100msResult');
  });
};

const rejectAsync100ms = () => {
  return new Promise((resolve, reject) => {
    setTimeout(reject, 100, 'reject100msError');
  });
};

const asyncInArray = async (fun1, fun2) => {
  const label = 'test async functions in array';
  try {
    console.time(label);
    const p1 = fun1();
    const p2 = fun2();
    const result = [await p1, await p2];
    console.timeEnd(label);
  } catch (e) {
    console.error('error is', e);
    console.timeEnd(label);
  }
};

const asyncInPromiseAll = async (fun1, fun2) => {
  const label = 'test async functions with Promise.all';
  try {
    console.time(label);
    let [value1, value2] = await Promise.all([fun1(), fun2()]);
    console.timeEnd(label);
  } catch (e) {
    console.error('error is', e);
    console.timeEnd(label);
  }
};

(async () => {
  console.group('async functions without error');
  console.log('async functions without error: start')
  await asyncInArray(correctAsync500ms, correctAsync100ms);
  await asyncInPromiseAll(correctAsync500ms, correctAsync100ms);
  console.groupEnd();

  console.group('async functions with error');
  console.log('async functions with error: start')
  await asyncInArray(correctAsync500ms, rejectAsync100ms);
  await asyncInPromiseAll(correctAsync500ms, rejectAsync100ms);
  console.groupEnd();
})();






babeljs