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


431

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

await someCall();
await anotherCall();

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

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

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


1
@adeneo यह गलत है, जावास्क्रिप्ट कभी भी अपने संदर्भ में समानांतर नहीं चलता है।
Blindman67

5
@ Blindman67 - यह करता है, कम से कम जिस तरह से ओपी का मतलब है, जहां दो async ऑपरेशन एक साथ चल रहे हैं, लेकिन इस मामले में, मेरा लिखने का मतलब यह नहीं था कि वे धारावाहिक में चलते हैं , पहले awaitपहले समारोह के पूरा होने का इंतजार करेंगे पूरी तरह से दूसरे को निष्पादित करने से पहले।
२०

3
@ Blindman67 - यह एकल पिरोया हुआ है, लेकिन यह सीमा async तरीकों पर लागू नहीं होती है, वे एक साथ चल सकते हैं, और जब वे कर रहे हैं, तो प्रतिक्रिया वापस कर सकते हैं, अर्थात ओपी का अर्थ "समानता" है।
एडीने

7
@ Blindman67 - मुझे लगता है कि यह स्पष्ट है कि OP क्या पूछ रहा है, async / प्रतीक्षारत पैटर्न का उपयोग करने से फ़ंक्शंस धारावाहिक में चलेंगे, भले ही वे async हों, इसलिए दूसरा पूरी तरह से समाप्त होने से पहले पूरी तरह से समाप्त हो जाएगा, आदि यह पूछने पर कि दोनों फ़ंक्शन को समानता में कैसे कॉल किया जा सकता है, और जैसा कि वे स्पष्ट रूप से async हैं, उद्देश्य उन्हें एक साथ चलाना है, यानी समानांतर में, दो ajax अनुरोधों को एक साथ करने के लिए, जो कि जावास्क्रिप्ट में बिल्कुल भी समस्या नहीं है, क्योंकि अधिकांश async तरीके , जैसा कि आपने नोट किया है, देशी कोड चलाता है, और अधिक थ्रेड का उपयोग करता है।
एडेनो

3
@Bergi यह लिंक किए गए प्रश्न का डुप्लिकेट नहीं है - यह विशेष रूप से async / प्रतीक्षा सिंटैक्स और देशी Promises के बारे में है। जुड़ा हुआ सवाल जनरेटर और उपज के साथ ब्लूबर्ड लाइब्रेरी के बारे में है। वैचारिक रूप से शायद समान है, लेकिन कार्यान्वयन में नहीं।
iest

जवाबों:


700

आप इंतजार कर सकते हैं Promise.all():

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

परिणामों को संग्रहीत करने के लिए:

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

ध्यान दें कि Promise.allतेजी से विफल हो जाता है, जिसका अर्थ है कि जैसे ही उसे दिया गया कोई भी वादे को अस्वीकार करता है, तो पूरी बात खारिज हो जाती है।

const happy = (v, ms) => new Promise((resolve) => setTimeout(() => resolve(v), ms))
const sad = (v, ms) => new Promise((_, reject) => setTimeout(() => reject(v), ms))

Promise.all([happy('happy', 100), sad('sad', 50)])
  .then(console.log).catch(console.log) // 'sad'

यदि, इसके बजाय, आप सभी वादों को पूरा करने या अस्वीकार करने के लिए इंतजार करना चाहते हैं, तो आप उपयोग कर सकते हैं Promise.allSettled। ध्यान दें कि इंटरनेट एक्सप्लोरर मूल रूप से इस पद्धति का समर्थन नहीं करता है।

const happy = (v, ms) => new Promise((resolve) => setTimeout(() => resolve(v), ms))
const sad = (v, ms) => new Promise((_, reject) => setTimeout(() => reject(v), ms))

Promise.allSettled([happy('happy', 100), sad('sad', 50)])
  .then(console.log) // [{ "status":"fulfilled", "value":"happy" }, { "status":"rejected", "reason":"sad" }]


78
स्वच्छ, लेकिन Promise.all के तेजी से विफल व्यवहार के बारे में पता होना चाहिए। यदि कोई भी कार्य त्रुटि करता है, तो
Promise.all

11
आप आंशिक रूप से async / प्रतीक्षा के साथ आंशिक रूप से परिणाम देख सकते हैं, देखें stackoverflow.com/a/42158854/2019689
NoNameProvided

131
प्रो टिप: Promise.all () से परिणामों की एक मनमानी संख्या को आरंभ करने के लिए सरणी विनाशकारी का उपयोग करें, जैसे:[result1, result2] = Promise.all([async1(), async2()]);
जॉनी

10
@ जॉनी क्या यह विषय फेल-फास्ट है? इसके अलावा, क्या अभी भी एक की जरूरत है = await Promise.all?
TheUtherSide

5
@theUtherSide आप बिलकुल सही हैं - मैंने वेट को शामिल करने की उपेक्षा की।
जॉनी

114

टी एल; डॉ

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


सबसे पहले, सभी अतुल्यकालिक कॉल को एक साथ निष्पादित करें और सभी Promiseऑब्जेक्ट प्राप्त करें। दूसरा, वस्तुओं awaitपर उपयोग करें 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()])निम्नलिखित कोड स्निपेट की जांच करें

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


11
यह Promise.all की तुलना में मेरे लिए एक बहुत अच्छे विकल्प की तरह दिखता है - और विनाशकारी असाइनमेंट के साथ आप भी कर सकते हैं [someResult, anotherResult] = [await someResult, await anotherResult]यदि आप बदल constजाते हैं let
13:25 बजे jawj

28
लेकिन यह अभी भी सिलसिलेवार awaitबयानों को निष्पादित करता है , है ना? यही है, निष्पादन पहले awaitहल होने तक रुकता है , फिर दूसरे पर चलता है। Promise.allसमानांतर में निष्पादित करता है।
एंडरू

8
धन्यवाद @ हवन। यह स्वीकृत उत्तर होना चाहिए।
स्टेफन डी

87
यह उत्तर इस तथ्य के रूप में भ्रामक है कि दोनों प्रतीक्षाएं एक ही पंक्ति में की जाती हैं अप्रासंगिक हैं। क्या मायने रखता है कि दो async कॉल या तो प्रतीक्षा करने से पहले किए जाते हैं।
जॉनीएचके

15
@ इस समाधान के रूप में ही नहीं है Promise.all। यदि प्रत्येक अनुरोध एक नेटवर्क कॉल है, तो await someResultइसे await anotherResultशुरू करने से पहले हल करने की आवश्यकता होगी । इसके विपरीत, Promise.allदोनों में awaitसे किसी एक को हल करने से पहले शुरू किया जा सकता है।
बेन विंडिंग

89

अपडेट करें:

मूल उत्तर यह वादा अस्वीकार को सही ढंग से संभालने के लिए (और कुछ मामलों में असंभव) मुश्किल बनाता है। सही समाधान का उपयोग करना है 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;

1
@JeffFischer मैंने ऐसी टिप्पणियाँ जोड़ीं जो उम्मीद से इसे स्पष्ट करती हैं।
जोनाथन पॉटर

9
मुझे ऐसा लगता है कि यह निश्चित रूप से सबसे शुद्ध उत्तर है
गेरशोम

1
यह उत्तर हेवन की तुलना में बहुत अधिक स्पष्ट है। यह स्पष्ट है कि फ़ंक्शन कॉल वादा ऑब्जेक्ट्स को लौटाएगा, और awaitफिर उन्हें वास्तविक मूल्यों में हल करेगा।
user1032613

3
यह एक सरसरी नज़र में काम करता प्रतीत होता है, लेकिन इसमें अस्वाभाविक अस्वीकारों के साथ भयानक समस्याएं हैंइस का उपयोग न करें!
बेर्गी

1
@ बर्गी आप सही कह रहे हैं, इस ओर इशारा करने के लिए धन्यवाद! मैंने बेहतर समाधान के साथ उत्तर को अपडेट किया है।
जोनाथन पॉटर

24

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

10

यह Promise.allSettled () के साथ पूरा किया जा सकता है , जो Promise.all()असफल-तेज व्यवहार के बिना भी समान है ।

async function failure() {
    throw "Failure!";
}

async function success() {
    return "Success!";
}

const [failureResult, successResult] = await Promise.allSettled([failure(), success()]);

console.log(failureResult); // {status: "rejected", reason: "Failure!"}
console.log(successResult); // {status: "fulfilled", value: "Success!"}

नोट : यह सीमित ब्राउज़र समर्थन के साथ एक रक्तस्रावी बढ़त सुविधा है, इसलिए मैं इस फ़ंक्शन के लिए पॉलीफ़िल सहित दृढ़ता से अनुशंसा करता हूं ।


7

मेरे द्वारा बनाए गए एक सार , को हल करने का वादा किया की कुछ अलग अलग तरीकों से परीक्षण परिणामों के साथ। यह उन विकल्पों को देखने में मददगार हो सकता है जो काम करते हैं।


जीआईएस में टेस्ट 4 और 6 ने अपेक्षित परिणाम लौटाए। NoNameProvided द्वारा stackoverflow.com/a/42158854/5683904 देखें जो विकल्पों के बीच का अंतर बताते हैं।
एकराइन्स

1
    // A generic test function that can be configured 
    // with an arbitrary delay and to either resolve or reject
    const test = (delay, resolveSuccessfully) => new Promise((resolve, reject) => setTimeout(() => {
        console.log(`Done ${ delay }`);
        resolveSuccessfully ? resolve(`Resolved ${ delay }`) : reject(`Reject ${ delay }`)
    }, delay));

    // Our async handler function
    const handler = async () => {
        // Promise 1 runs first, but resolves last
        const p1 = test(10000, true);
        // Promise 2 run second, and also resolves
        const p2 = test(5000, true);
        // Promise 3 runs last, but completes first (with a rejection) 
        // Note the catch to trap the error immediately
        const p3 = test(1000, false).catch(e => console.log(e));
        // Await all in parallel
        const r = await Promise.all([p1, p2, p3]);
        // Display the results
        console.log(r);
    };

    // Run the handler
    handler();
    /*
    Done 1000
    Reject 1000
    Done 5000
    Done 10000
    */

पी 1, पी 2 और पी 3 सेट करते समय उन्हें कड़ाई से समानांतर में नहीं चलाया जाता है, वे किसी भी निष्पादन को पकड़ नहीं पाते हैं और आप एक कैच के साथ प्रासंगिक त्रुटियों को फंसा सकते हैं।


2
ढेर अतिप्रवाह में आपका स्वागत है। जबकि आपका कोड प्रश्न का उत्तर प्रदान कर सकता है, कृपया इसके चारों ओर संदर्भ जोड़ें, ताकि दूसरों को कुछ विचार हो कि यह क्या करता है और यह क्यों है।
थियो

1

मेरे मामले में, मेरे पास कई कार्य हैं जिन्हें मैं समानांतर में निष्पादित करना चाहता हूं, लेकिन मुझे उन कार्यों के परिणाम के साथ कुछ अलग करने की आवश्यकता है।

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

var tasks = [
    async () => {
        var result = await wait(1000, 'moose');
        // do something with result
        console.log(result);
    },
    async () => {
        var result = await wait(500, 'taco');
        // do something with result
        console.log(result);
    },
    async () => {
        var result = await wait(5000, 'burp');
        // do something with result
        console.log(result);
    }
]

await Promise.all(tasks.map(p => p()));
console.log('done');

और आउटपुट:

Starting task: moose 1000
Starting task: taco 500
Starting task: burp 5000
taco
moose
burp
done

गतिशील निर्माण (संसाधनों की सरणी) के लिए शांत
मिखल मिकी जानकोव्स्की

1

Promit.all ([someCall (), otherCall ()]) का इंतजार करें; जैसा कि पहले ही उल्लेख किया गया है, एक थ्रेड बाड़ के रूप में कार्य करेगा (CUDA के रूप में समानांतर कोड में बहुत आम है), इसलिए यह इसमें सभी वादों को एक दूसरे को अवरुद्ध किए बिना चलाने की अनुमति देगा, लेकिन जब तक सभी हल नहीं हो जाते तब तक निष्पादन को रोकना होगा।

एक और दृष्टिकोण जो साझा करने के लिए लायक है, वह है Node.js async जो आपको आसानी से संगणक की मात्रा को नियंत्रित करने की अनुमति देगा, जो आमतौर पर वांछनीय है यदि कार्य सीधे एपीआई कॉल, आई / ओ संचालन के रूप में सीमित संसाधनों के उपयोग से जुड़ा हुआ है, आदि।

// create a queue object with concurrency 2
var q = async.queue(function(task, callback) {
  console.log('Hello ' + task.name);
  callback();
}, 2);

// assign a callback
q.drain = function() {
  console.log('All items have been processed');
};

// add some items to the queue
q.push({name: 'foo'}, function(err) {
  console.log('Finished processing foo');
});

q.push({name: 'bar'}, function (err) {
  console.log('Finished processing bar');
});

// add some items to the queue (batch-wise)
q.push([{name: 'baz'},{name: 'bay'},{name: 'bax'}], function(err) {
  console.log('Finished processing item');
});

// add some items to the front of the queue
q.unshift({name: 'bar'}, function (err) {
  console.log('Finished processing bar');
});

मध्यम लेख ऑटोर को क्रेडिट (और पढ़ें )


-5

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

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

चूंकि आप हालत परीक्षण से पहले / उसके बाहर के कार्य को घोषित करते हैं, और उन्हें कहते हैं। उन्हें elseब्लॉक में लपेटने का प्रयास करें ।
हेवन

@ हेवेन: मेरा मतलब है कि जब आप उन कार्यों को अलग कर लेते हैं जिन्हें आप कॉल करते हैं, तो प्रतीक्षारत अप्रत्याशित परिणाम हो सकते हैं, उदाहरण के लिए: async HTTP अनुरोध।
होआंग ले अन्ह तु

-6

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

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

3
नहीं, यहाँ पर समानांतरीकरण नहीं हो रहा है। forपाश क्रमिक रूप से प्रत्येक वादा इंतजार कर रहा है और सरणी के लिए परिणाम कहते हैं।
स्ज़ेपेपैन होल्सिज़ेस्की

मैं समझता हूं कि यह लोगों के लिए काम नहीं कर रहा है। इसलिए मैंने नोड.जेएस और ब्राउज़र में परीक्षण किया। परीक्षण नोड (वी 10, वी 11), फ़ायरफ़ॉक्स में पारित किया गया है, यह ब्राउज़र क्रोम में काम नहीं करता है। परीक्षण का मामला gist.github.com/fredyang/ea736a7b8293edf7a1a25c39c7d2fbbf पर है
फ्रेड यांग

2
मैं इस पर विश्वास करने से इनकार करता हूं। मानक में ऐसा कुछ भी नहीं है जो कहता है कि लूप के विभिन्न पुनरावृत्तियों को स्वचालित रूप से समानांतर किया जा सकता है; यह नहीं है कि जावास्क्रिप्ट कैसे काम करता है। जिस तरह से लूप कोड लिखा गया है, इसका मतलब यह है: "एक आइटम का इंतजार करें (प्रतीक्षित विस्तार), फिर से शुरू करने के लिए धक्का परिणाम, तब अगले आइटम (लूप के लिए अगला चलना) ले। प्रत्येक आइटम के लिए" प्रतीक्षा "पूरी तरह से है। लूप के एक एकल पुनरावृत्ति तक सीमित। यदि परीक्षणों से पता चलता है कि समानांतर है, तो इसका कारण यह होना चाहिए कि ट्रांसपॉयलर कुछ नॉनस्टैंडर्ड है या बुग्गी से बाहर है।
Szczepan Hołyszewski

@ SzczepanHołyszewski परीक्षण मामले को चलाने के बिना मानने का आपका विश्वास मुझे कुछ नाम बदलने और अतिरिक्त टिप्पणियों के लिए प्रेरित करता है। सभी कोड सादे पुराने ES6 हैं, किसी भी ट्रांसप्लिंग की आवश्यकता नहीं है।
फ्रेड यांग

यकीन नहीं होता कि यह इतना भारी क्यों है। यह अनिवार्य रूप से वही उत्तर है जो @ user2883596 ने दिया था।
जोनाथन सुदियामैन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.