Node.js देशी प्रोमिस है। समानांतर या क्रमिक रूप से सभी प्रसंस्करण?


173

मैं इस बिंदु को स्पष्ट करना चाहूंगा, क्योंकि प्रलेखन इसके बारे में बहुत स्पष्ट नहीं है;

Q1: है Promise.all(iterable)प्रसंस्करण सभी वादों क्रमिक रूप से या समानांतर में? या, विशेष रूप से, यह जंजीरों से चलने वाले वादों के बराबर है

p1.then(p2).then(p3).then(p4).then(p5)....

या यह एल्गोरिथ्म के कुछ अन्य प्रकार है, जहां सभी है p1, p2, p3, p4, p5, आदि एक ही समय में (समानांतर में) कहा जा रहा है और परिणाम सभी संकल्प (या एक अस्वीकृत) के रूप में के रूप में जल्द वापस आ रहे हैं?

Q2: यदि Promise.allसमानांतर में चलता है, तो क्या पुनरावृत्त क्रम चलने के लिए एक सुविधाजनक तरीका है?

नोट : मैं Q, या Bluebird का उपयोग नहीं करना चाहता, लेकिन सभी मूल ES6 चश्मा।


क्या आप नोड (V8) कार्यान्वयन के बारे में पूछ रहे हैं, या कल्पना के बारे में?
अमित

1
मुझे पूरा यकीन है कि Promise.allउन्हें समानांतर में निष्पादित किया जाएगा।
रोहिणी

@Amit मैंने झंडी दिखा दी node.jsऔर io.jsजैसा कि मैं इसका उपयोग कर रहा हूं। तो, हाँ, V8 कार्यान्वयन यदि आप करेंगे।
यानिक रोचॉन

9
वादे "निष्पादित नहीं किए जा सकते"। वे अपना कार्य तब शुरू करते हैं जब वे बनाए जा रहे होते हैं - वे केवल परिणामों का प्रतिनिधित्व करते हैं - और आप उन्हें पारित करने से पहले ही समानांतर में सब कुछ निष्पादित कर रहे हैं Promise.all
बरगी

सृजन के क्षण में वादों को निष्पादित किया जाता है। (थोड़ा सा कोड चलाकर पुष्टि की जा सकती है)। में new Promise(a).then(b); c();पहली बार एक को मार डाला है, तो सी, तो ख। यह वादा नहीं है। इन वादों को चलाने वाला, यह बस हल होने पर संभालता है।
Mateon1

जवाबों:


257

है Promise.all(iterable) सभी वादों को क्रियान्वित?

नहीं, वादे "निष्पादित नहीं किए जा सकते"। जब वे बनाए जा रहे हैं तो वे अपना काम शुरू करते हैं - वे केवल परिणामों का प्रतिनिधित्व करते हैं - और आप उन्हें पारित करने से पहले ही समानांतर में सब कुछ निष्पादित कर रहे हैं Promise.all

Promise.allकेवल कई वादों का इंतजार करता है। यह इस बात की परवाह नहीं करता है कि वे किस क्रम में हल करते हैं, या क्या गणना समानांतर में चल रही है।

वहाँ एक iterable क्रम चलाने के लिए एक सुविधाजनक तरीका है?

यदि आपके पास पहले से ही अपने वादे हैं, तो आप बहुत कुछ नहीं कर सकते हैं, लेकिन Promise.all([p1, p2, p3, …])(जिसमें अनुक्रम की धारणा नहीं है)। लेकिन अगर आपके पास अतुल्यकालिक कार्यों का चलना है, तो आप वास्तव में उन्हें क्रमिक रूप से चला सकते हैं। मूल रूप से आपको प्राप्त करने की आवश्यकता है

[fn1, fn2, fn3, …]

सेवा

fn1().then(fn2).then(fn3).then(…)

और इसका उपयोग करने के लिए समाधान Array::reduce:

iterable.reduce((p, fn) => p.then(fn), Promise.resolve())

1
इस उदाहरण में, क्या यह उन कार्यों का एक सरणी है जो एक वादा वापस करते हैं जिसे आप कॉल करना चाहते हैं?
जेम्स रीतेग्युई

2
@SSHThis: यह बिल्कुल thenअनुक्रम के रूप में है - अंतिम मूल्य अंतिम fnपरिणाम के लिए वादा है , और आप उस पर अन्य कॉलबैक को चेन कर सकते हैं।
बरगी

1
@wojjas यह बिल्कुल बराबर है fn1().then(p2).then(fn3).catch(…? फ़ंक्शन अभिव्यक्ति का उपयोग करने की आवश्यकता नहीं है।
बरगी

1
@wojjas के पाठ्यक्रम में retValFromF1पारित किया है p2, कि वास्तव में क्या p2है। निश्चित रूप से, यदि आप और अधिक करना चाहते हैं (अतिरिक्त चर पास करें, कई फ़ंक्शन कॉल करें, आदि) तो आपको एक फ़ंक्शन अभिव्यक्ति का उपयोग करने की आवश्यकता है, हालांकि p2सरणी में बदलना आसान होगा
बर्गी

1
@ robe007 हां, मेरा मतलब था कि iterableयह [fn1, fn2, fn3, …]सरणी है
बर्गी

62

समान्तर में

await Promise.all(items.map(async item => { await fetchItem(item) }))

लाभ: तेज़। यदि कोई विफल रहता है, तो सभी पुनरावृत्तियों को निष्पादित किया जाएगा।

क्रम में

for (let i = 0; i < items.length; i++) {
    await fetchItem(items[i])
}

लाभ: लूप में चर प्रत्येक पुनरावृत्ति द्वारा साझा किए जा सकते हैं। सामान्य अनिवार्य तुल्यकालिक कोड की तरह व्यवहार करता है।


7
या:for (const item of items) await fetchItem(item);
रॉबर्ट पेनर

1
@david_adler समानांतर उदाहरण के लाभों में आपने कहा था कि यदि कोई विफल हो जाता है तो सभी पुनरावृत्तियों को निष्पादित किया जाएगा । अगर मैं गलत नहीं हूँ, यह अभी भी तेजी से विफल हो जाएगा। इस व्यवहार को बदलने के लिए व्यक्ति कुछ ऐसा कर सकता है: await Promise.all(items.map(async item => { return await fetchItem(item).catch(e => e) }))
तैमूर

@Taimoor हाँ, यह "तेजी से विफल" होता है और Promise.all के बाद कोड निष्पादित करना जारी रखता है, लेकिन सभी पुनरावृत्तियों को अभी भी निष्पादित किया जाता है codepen.io/mfbx9da4/pen/BbaaXr
davidadadler

यह दृष्टिकोण बेहतर है, जब asyncफ़ंक्शन एक एपीआई कॉल है जिसे आप सर्वर को डीडीओएस नहीं करना चाहते हैं। निष्पादन में फेंके गए व्यक्तिगत परिणामों और त्रुटियों पर आपका बेहतर नियंत्रण है। इससे भी बेहतर आप यह तय कर सकते हैं कि क्या त्रुटियों को जारी रखना है और क्या लूप को तोड़ना है।
मंदारिन

ध्यान दें कि जावास्क्रिप्ट वास्तव में धागे के उपयोग से "समानांतर" में अतुल्यकालिक अनुरोधों को निष्पादित नहीं कर रहा है क्योंकि जावास्क्रिप्ट एकल थ्रेडेड है। developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop
david_adler

11

बर्गिस का जवाब मुझे Array.reduce का उपयोग करके सही रास्ते पर मिला।

हालाँकि, वास्तव में एक के बाद एक निष्पादित करने के लिए अपने वादों को वापस करने वाले कार्यों को प्राप्त करने के लिए मुझे कुछ और घोंसले के शिकार को जोड़ना पड़ा।

मेरा वास्तविक उपयोग मामला फाइलों की एक सरणी है जिसे मुझे डाउनस्ट्रीम सीमा के कारण एक के बाद एक क्रम में स्थानांतरित करने की आवश्यकता है ...

यहाँ है क्या मैं के साथ समाप्त हुआ।

getAllFiles().then( (files) => {
    return files.reduce((p, theFile) => {
        return p.then(() => {
            return transferFile(theFile); //function returns a promise
        });
    }, Promise.resolve()).then(()=>{
        console.log("All files transferred");
    });
}).catch((error)=>{
    console.log(error);
});

जैसा कि पिछले उत्तर सुझाते हैं:

getAllFiles().then( (files) => {
    return files.reduce((p, theFile) => {
        return p.then(transferFile(theFile));
    }, Promise.resolve()).then(()=>{
        console.log("All files transferred");
    });
}).catch((error)=>{
    console.log(error);
});

दूसरे को शुरू करने से पहले स्थानांतरण के पूरा होने का इंतजार न करें और पहले फ़ाइल स्थानांतरण शुरू होने से पहले "सभी फ़ाइलें स्थानांतरित" पाठ भी आए।

मुझे यकीन नहीं है कि मैंने क्या गलत किया है, लेकिन जो मेरे लिए काम करता था उसे साझा करना चाहता था।

संपादित करें: चूंकि मैंने इस पोस्ट को लिखा था इसलिए अब मुझे समझ में आया कि पहला संस्करण क्यों काम नहीं किया। तो () एक उम्मीद समारोह एक वादा लौटने। तो, आप कोष्ठक के बिना फ़ंक्शन नाम में पास होना चाहिए! अब, मेरा फ़ंक्शन एक तर्क चाहता है, तो मुझे कोई तर्क नहीं लेते हुए एक अनाम फ़ंक्शन में लपेटने की आवश्यकता है!


4

सिर्फ @ बर्गी के उत्तर पर विस्तार से बताएं (जो बहुत ही रसीला है, लेकिन समझने में मुश्किल है;)

यह कोड सरणी में प्रत्येक आइटम को चलाएगा और अंत में अगली 'फिर चेन' को जोड़ देगा;

function eachorder(prev,order) {
        return prev.then(function() {
          return get_order(order)
            .then(check_order)
            .then(update_order);
        });
    }
orderArray.reduce(eachorder,Promise.resolve());

आशा है कि समझ में आता है।


3

आप पुनरावर्ती फ़ंक्शन का उपयोग करके किसी async फ़ंक्शन के साथ क्रमिक रूप से पुनरावृत्ति को भी संसाधित कर सकते हैं। उदाहरण के लिए, aअतुल्यकालिक फ़ंक्शन के साथ प्रक्रिया करने के लिए एक सरणी दी गई है someAsyncFunction():

var a = [1, 2, 3, 4, 5, 6]

function someAsyncFunction(n) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log("someAsyncFunction: ", n)
      resolve(n)
    }, Math.random() * 1500)
  })
}

//You can run each array sequentially with: 

function sequential(arr, index = 0) {
  if (index >= arr.length) return Promise.resolve()
  return someAsyncFunction(arr[index])
    .then(r => {
      console.log("got value: ", r)
      return sequential(arr, index + 1)
    })
}

sequential(a).then(() => console.log("done"))


array.prototype.reduceएक पुनरावर्ती कार्य की तुलना में प्रदर्शन के मामले में उपयोग करना बेहतर है
माटुस्ज़ सोविस्की

@ MateuszSowiński, प्रत्येक कॉल के बीच 1500ms का समयबाह्य है। यह देखते हुए कि यह async कॉल को क्रमिक रूप से कर रहा है, यह देखना बहुत कठिन है कि यह बहुत जल्दी async टर्नअराउंड के लिए भी प्रासंगिक कैसे है।
मार्क मेयर

मान लीजिए कि आपको एक दूसरे के बाद वास्तव में त्वरित async कार्यों के 40 को निष्पादित करना है - पुनरावर्ती कार्यों का उपयोग करके आपकी स्मृति को बहुत तेजी से रोकना होगा
Mateusz Sowiński

@ MateuszSowiński, कि स्टैक यहाँ हवा नहीं है ... हम प्रत्येक कॉल के बाद लौट रहे हैं। उस के साथ तुलना करें reduceजहां आपको then()एक चरण में पूरी श्रृंखला का निर्माण करना है और फिर निष्पादित करना है।
मार्क मेयेर

अनुक्रमिक फ़ंक्शन के 40 वें कॉल में फ़ंक्शन का पहला कॉल अभी भी स्मृति में है जो अनुक्रमिक कार्यों की श्रृंखला के लौटने की प्रतीक्षा कर रहा है
Mateusz Sowiński

2

एस्किंट वेट का उपयोग करके वादों की एक सरणी का आसानी से क्रमिक रूप से क्रियान्वयन किया जा सकता है:

let a = [promise1, promise2, promise3];

async function func() {
  for(let i=0; i<a.length; i++){
    await a[i]();
  }  
}

func();

नोट: उपरोक्त कार्यान्वयन में, यदि कोई वादा खारिज कर दिया जाता है, तो बाकी को निष्पादित नहीं किया जाएगा। यदि आप चाहते हैं कि आपके सभी वादों को निष्पादित किया जाए, तो अपने await a[i]();अंदर लपेटेंtry catch


2

समानांतर

इस उदाहरण को देखें

const resolveAfterTimeout = async i => {
  return new Promise(resolve => {
    console.log("CALLED");
    setTimeout(() => {
      resolve("RESOLVED", i);
    }, 5000);
  });
};

const call = async () => {
  const res = await Promise.all([
    resolveAfterTimeout(1),
    resolveAfterTimeout(2),
    resolveAfterTimeout(3),
    resolveAfterTimeout(4),
    resolveAfterTimeout(5),
    resolveAfterTimeout(6)
  ]);
  console.log({ res });
};

call();

कोड चलाने से यह सभी छह वादों के लिए "CALLED" को सांत्वना देगा और जब वे हल हो जाएंगे तो एक ही समय में टाइमआउट के बाद हर 6 प्रतिक्रियाओं को सांत्वना देंगे।


2

NodeJS समानांतर में वादे नहीं चलाता है, यह उन्हें समवर्ती रूप से चलाता है क्योंकि यह एक एकल थ्रेडेड इवेंट लूप आर्किटेक्चर है। मल्टीपल कोर सीपीयू का लाभ उठाने के लिए एक नई चाइल्ड प्रक्रिया बनाकर समानांतर चीजों को चलाने की संभावना है।

समानांतर बनाम कॉन्सुरेंट

वास्तव में, क्या Promise.all होता है, वादे फ़ंक्शन को उचित कतार में (इवेंट लूप आर्किटेक्चर देखें) स्टैकिंग करते हुए उन्हें समवर्ती रूप से चलाएं (पी 1, पी 2, ... कॉल करें) फिर प्रत्येक परिणाम की प्रतीक्षा करें, फिर सभी वादों के साथ Promise.all को हल करें। परिणाम है। जब तक आप अस्वीकृति का प्रबंधन खुद नहीं करेंगे, वादा करो।

समानांतर और समवर्ती के बीच एक बड़ा अंतर है, पहला एक ही समय में अलग-अलग प्रक्रिया में अलग-अलग गणना चलाएगा और वे वहाँ rythme पर प्रगति करेंगे, जबकि दूसरा पहले की प्रतीक्षा किए बिना एक के बाद एक अलग गणना को निष्पादित करेगा एक दूसरे के आधार पर एक ही समय में समाप्त करने और प्रगति करने के लिए अभिकलन।

अंत में, अपने प्रश्न का उत्तर देने के लिए, Promise.allन तो समानांतर या क्रमिक रूप से, बल्कि समवर्ती रूप से निष्पादित करेंगे।


यह सही नहीं है। NodeJS समानांतर में चीजों को चला सकते हैं। NodeJS में श्रमिक सूत्र की अवधारणा है। डिफ़ॉल्ट रूप से वर्कर थ्रेड की संख्या 4 है। उदाहरण के लिए, यदि आप दो मानों को क्रिप्टो लाइब्रेरी का उपयोग करते हैं तो आप उन्हें समानांतर में निष्पादित कर सकते हैं। दो श्रमिक सूत्र कार्य को संभालेंगे। बेशक, समानांतरता का समर्थन करने के लिए आपके CPU को मल्टी-कोर होना चाहिए।
शिहाब

हाँ, आपने सही कहा, यह मैंने पहले पैराग्राफ के अंत में कहा था, लेकिन मैंने बच्चे की प्रक्रिया के बारे में बात की, बेशक वे श्रमिकों को चला सकते हैं।
Adrien De Peretti

1

बर्गी के जवाब ने मुझे कॉल को तुल्यकालिक बनाने में मदद की। मैंने नीचे एक उदाहरण जोड़ा है जहां हम पिछले फ़ंक्शन को कॉल करने के बाद प्रत्येक फ़ंक्शन को कॉल करते हैं।

function func1 (param1) {
    console.log("function1 : " + param1);
}
function func2 () {
    console.log("function2");
}
function func3 (param2, param3) {
    console.log("function3 : " + param2 + ", " + param3);
}

function func4 (param4) {
    console.log("function4 : " + param4);
}
param4 = "Kate";

//adding 3 functions to array

a=[
    ()=>func1("Hi"),
    ()=>func2(),
    ()=>func3("Lindsay",param4)
  ];

//adding 4th function

a.push(()=>func4("dad"));

//below does func1().then(func2).then(func3).then(func4)

a.reduce((p, fn) => p.then(fn), Promise.resolve());

क्या यह मूल प्रश्न का उत्तर है?
Giulio Caccin

0

आप इसे लूप के लिए कर सकते हैं।

async फ़ंक्शन रिटर्न वादा

async function createClient(client) {
    return await Client.create(client);
}

let clients = [client1, client2, client3];

यदि आप निम्नलिखित कोड लिखते हैं तो क्लाइंट समानांतर रूप से बनाए जाते हैं

const createdClientsArray = yield Promise.all(clients.map((client) =>
    createClient(client);
));

तब सभी क्लाइंट समानांतर रूप से बनाए जाते हैं। लेकिन अगर आप क्लाइंट को क्रमिक रूप से बनाना चाहते हैं तो आपको लूप का उपयोग करना चाहिए

const createdClientsArray = [];
for(let i = 0; i < clients.length; i++) {
    const createdClient = yield createClient(clients[i]);
    createdClientsArray.push(createdClient);
}

फिर सभी क्लाइंट क्रमिक रूप से बनाए जाते हैं।

खुश कोडिंग :)


8
इस समय, async/ awaitकेवल एक ट्रांसपिलर के साथ उपलब्ध है, या नोड के अलावा अन्य इंजनों का उपयोग कर रहा है । इसके अलावा, आप वास्तव में मिश्रण नहीं किया जाना चाहिए asyncके साथ yield। वे एक ट्रांसपिलर के साथ एक ही कार्य करते हैं और co, वे वास्तव में काफी अलग हैं और आमतौर पर एक-दूसरे को प्रतिस्थापित नहीं करना चाहिए। इसके अलावा, आपको इन प्रतिबंधों का उल्लेख करना चाहिए क्योंकि आपका उत्तर नौसिखिए प्रोग्रामर को भ्रमित कर रहा है।
यानिक रोचॉन

0

मैं क्रमिक वादों को हल करने के लिए उपयोग कर रहा हूं। मुझे यकीन नहीं है कि यह यहाँ मदद करता है लेकिन यह वही है जो मैं कर रहा हूँ।

async function run() {
    for (let val of arr) {
        const res = await someQuery(val)
        console.log(val)
    }
}

run().then().catch()

0

यह आपके प्रश्न का हिस्सा हो सकता है।

हां, आप वादे को पूरा करने वाले कार्यों की एक श्रृंखला की श्रृंखला बना सकते हैं ... (यह प्रत्येक फ़ंक्शन के परिणाम को अगले तक पहुंचाता है)। आप निश्चित रूप से प्रत्येक फ़ंक्शन के लिए एक ही तर्क (या कोई तर्क) पास करने के लिए इसे संपादित कर सकते हैं।

function tester1(a) {
  return new Promise(function(done) {
    setTimeout(function() {
      done(a + 1);
    }, 1000);
  })
}

function tester2(a) {
  return new Promise(function(done) {
    setTimeout(function() {
      done(a * 5);
    }, 1000);
  })
}

function promise_chain(args, list, results) {

  return new Promise(function(done, errs) {
    var fn = list.shift();
    if (results === undefined) results = [];
    if (typeof fn === 'function') {
      fn(args).then(function(result) {
        results.push(result);
        console.log(result);
        promise_chain(result, list, results).then(done);
      }, errs);
    } else {
      done(results);
    }

  });

}

promise_chain(0, [tester1, tester2, tester1, tester2, tester2]).then(console.log.bind(console), console.error.bind(console));


0

मैं NodeJS में एक समस्या को हल करने की कोशिश करते समय इस पृष्ठ पर ठोकर खाई: फ़ाइल विखंडू का पुनर्मूल्यांकन। मूल रूप से: मेरे पास फ़ाइलनाम का एक सरणी है। मुझे एक बड़ी फ़ाइल बनाने के लिए, उन सभी फाइलों को सही क्रम में जोड़ना होगा। मुझे यह अनिवार्य रूप से करना चाहिए।

नोड का 'fs' मॉड्यूल appendFileSync प्रदान करता है, लेकिन मैं इस ऑपरेशन के दौरान सर्वर को ब्लॉक नहीं करना चाहता था। मैं fspromises मॉड्यूल का उपयोग करना चाहता था और इस सामान को एक साथ चेन करने का तरीका ढूंढता था। इस पृष्ठ पर दिए गए उदाहरणों ने मेरे लिए बहुत काम नहीं किया क्योंकि मुझे वास्तव में दो कार्यों की आवश्यकता थी: फ़ाइल chunk में पढ़ने के लिए fsPromises.read (), और गंतव्य फ़ाइल पर जाने के लिए fsPromises.appendFile ()। हो सकता है कि अगर मैं जावास्क्रिप्ट के साथ बेहतर होता, तो मैं पिछले उत्तरों को मेरे लिए काम कर सकता था। ;-)

मैं इस पर ठोकर खाई ... https://css-tricks.com/why-use-reduce-to-fterentially-resolve-promises-works/ ... और मैं एक साथ काम कर रहे समाधान को हैक करने में सक्षम था।

TLDR:

/**
 * sequentially append a list of files into a specified destination file
 */
exports.append_files = function (destinationFile, arrayOfFilenames) {
    return arrayOfFilenames.reduce((previousPromise, currentFile) => {
        return previousPromise.then(() => {
            return fsPromises.readFile(currentFile).then(fileContents => {
                return fsPromises.appendFile(destinationFile, fileContents);
            });
        });
    }, Promise.resolve());
};

और यहाँ इसके लिए चमेली इकाई परीक्षण है:

const fsPromises = require('fs').promises;
const fsUtils = require( ... );
const TEMPDIR = 'temp';

describe("test append_files", function() {
    it('append_files should work', async function(done) {
        try {
            // setup: create some files
            await fsPromises.mkdir(TEMPDIR);
            await fsPromises.writeFile(path.join(TEMPDIR, '1'), 'one');
            await fsPromises.writeFile(path.join(TEMPDIR, '2'), 'two');
            await fsPromises.writeFile(path.join(TEMPDIR, '3'), 'three');
            await fsPromises.writeFile(path.join(TEMPDIR, '4'), 'four');
            await fsPromises.writeFile(path.join(TEMPDIR, '5'), 'five');

            const filenameArray = [];
            for (var i=1; i < 6; i++) {
                filenameArray.push(path.join(TEMPDIR, i.toString()));
            }

            const DESTFILE = path.join(TEMPDIR, 'final');
            await fsUtils.append_files(DESTFILE, filenameArray);

            // confirm "final" file exists    
            const fsStat = await fsPromises.stat(DESTFILE);
            expect(fsStat.isFile()).toBeTruthy();

            // confirm content of the "final" file
            const expectedContent = new Buffer('onetwothreefourfive', 'utf8');
            var fileContents = await fsPromises.readFile(DESTFILE);
            expect(fileContents).toEqual(expectedContent);

            done();
        }
        catch (err) {
            fail(err);
        }
        finally {
        }
    });
});

मुझे उम्मीद है कि यह किसी की मदद करता है।

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