एक forEach लूप के साथ async / प्रतीक्षा का उपयोग करना


1128

वहाँ का उपयोग कर के साथ किसी भी मुद्दे हैं async/ awaitएक में forEachपाश? मैं फ़ाइलों की एक सरणी के माध्यम से और awaitप्रत्येक फ़ाइल की सामग्री पर लूप करने की कोशिश कर रहा हूं ।

import fs from 'fs-promise'

async function printFiles () {
  const files = await getFilePaths() // Assume this works fine

  files.forEach(async (file) => {
    const contents = await fs.readFile(file, 'utf8')
    console.log(contents)
  })
}

printFiles()

यह कोड काम करता है, लेकिन इसके साथ कुछ गलत हो सकता है? मैंने किसी को बताया था कि आप इस तरह के एक उच्च क्रम फ़ंक्शन का उपयोग async/ उपयोग करने वाले नहीं हैं await, इसलिए मैं सिर्फ यह पूछना चाहता था कि क्या इसके लिए कोई समस्या थी।

जवाबों:


2143

यकीन है कि कोड काम करता है, लेकिन मुझे पूरा यकीन है कि यह वह नहीं करता है जो आप इसे करने की उम्मीद करते हैं। यह सिर्फ कई अतुल्यकालिक कॉल बंद करता है, लेकिन printFilesफ़ंक्शन उसके तुरंत बाद वापस लौट जाता है।

क्रम से पढ़ना

यदि आप फ़ाइलों को अनुक्रम में पढ़ना चाहते हैं, तो आपforEach वास्तव में उपयोग नहीं कर सकतेfor … ofइसके बजाय बस एक आधुनिक लूप का उपयोग करें, जिसमें awaitउम्मीद के मुताबिक काम होगा:

async function printFiles () {
  const files = await getFilePaths();

  for (const file of files) {
    const contents = await fs.readFile(file, 'utf8');
    console.log(contents);
  }
}

समानांतर में पढ़ना

यदि आप फ़ाइलों को समानांतर में पढ़ना चाहते हैं, तो आपforEach वास्तव में उपयोग नहीं कर सकतेasyncकॉलबैक फ़ंक्शन कॉल में से प्रत्येक एक वादा वापस करता है, लेकिन आप उन्हें प्रतीक्षा करने के बजाय दूर फेंक रहे हैं। mapइसके बजाय उपयोग करें , और आप उन वादों की प्रतीक्षा कर सकते हैं जो आपको मिलेंगे Promise.all:

async function printFiles () {
  const files = await getFilePaths();

  await Promise.all(files.map(async (file) => {
    const contents = await fs.readFile(file, 'utf8')
    console.log(contents)
  }));
}

33
क्या आप बता सकते हैं कि for ... of ...काम क्यों करता है ?
Demonbane

84
ठीक है, मुझे पता है क्यों ... बैबेल का उपयोग जनरेटर फ़ंक्शन को बदल देगा async/ करेगा awaitऔर forEachइसका मतलब है कि प्रत्येक पुनरावृत्ति में एक व्यक्तिगत जनरेटर फ़ंक्शन है, जिसका दूसरों के साथ कोई लेना-देना नहीं है। इसलिए उन्हें स्वतंत्र रूप से निष्पादित किया जाएगा और next()दूसरों के साथ कोई संदर्भ नहीं होगा । दरअसल, एक साधारण for()लूप भी काम करता है क्योंकि पुनरावृत्तियों एक एकल जनरेटर फ़ंक्शन में भी होते हैं।
डेमनबाने

21
@Demonbane: संक्षेप में, क्योंकि यह काम करने के लिए डिज़ाइन किया गया था :-) सभी फ़ंक्शन संरचनाओं सहित awaitवर्तमान फ़ंक्शन मूल्यांकन को निलंबित करता है । हां, यह उस संबंध में जनरेटर के समान है (यही कारण है कि वे async / प्रतीक्षा करने के लिए पॉलीफ़िल के लिए उपयोग किए जाते हैं)।
बेर्गी

3
@ arve0 वास्तव में नहीं, एक asyncफ़ंक्शन Promiseनिष्पादक कॉलबैक से काफी अलग है , लेकिन हां mapकॉलबैक दोनों मामलों में एक वादा वापस करता है।
बर्गी

5
जब आप जेएस वादों के बारे में जानने के लिए आते हैं, लेकिन इसके बजाय आधे घंटे का लैटिन अनुवाद करते हैं। आशा है कि आपको गर्व है @Bergi;)
फेलिक्स गगनोन-ग्रेनियर

188

ES2018 के साथ, आप उपरोक्त सभी उत्तरों को सरल बनाने में सक्षम हैं:

async function printFiles () {
  const files = await getFilePaths()

  for await (const file of fs.readFile(file, 'utf8')) {
    console.log(contents)
  }
}

कल्पना देखें: प्रस्ताव-एसिंक्स-पुनरावृत्ति


2018-09-10: इस उत्तर पर हाल ही में बहुत ध्यान दिया जा रहा है, कृपया अतुल्यकालिक पुनरावृत्ति के बारे में अधिक जानकारी के लिए एक्सल रौशमेयर के ब्लॉग पोस्ट को देखें: ES2018: अतुल्यकालिक पुनरावृत्ति


4
अपवोट, महान होगा यदि आप अपने जवाब में कल्पना के लिए एक लिंक डाल सकते हैं जो किसी के लिए भी है जो async पुनरावृत्ति के बारे में अधिक जानना चाहता है।
सादिक

8
क्या यह फ़ाइल के बजाय
इट्रेटर

10
लोग इस उत्तर को क्यों बढ़ा रहे हैं? उत्तर, प्रश्न और प्रस्ताव पर करीब से नज़र डालें। बाद ofasync फ़ंक्शन होना चाहिए जो एक सरणी लौटाएगा। यह काम नहीं करता है और फ्रांसिस्को ने कहा;
येवन्हि हेरासिमचुक ev ’

3
पूरी तरह से @AntonioVal से सहमत हैं। इसका जवाब नहीं है।
येवहनी हेरसिमचुक

2
जबकि मैं मानता हूं कि यह एक जवाब नहीं है, एक प्रस्ताव को आगे बढ़ाना इसकी लोकप्रियता बढ़ाने का एक तरीका है जो संभवतः बाद में इसे पहले उपयोग करने के लिए उपलब्ध कराता है।
रॉबर्ट मोलिना

61

Promise.allके साथ संयोजन के बजाय Array.prototype.map(जो उस क्रम की गारंटी नहीं देता है जिसमें Promiseएस हल किया गया है), मैं उपयोग करता हूं Array.prototype.reduce, एक हल के साथ शुरू होता है Promise:

async function printFiles () {
  const files = await getFilePaths();

  await files.reduce(async (promise, file) => {
    // This line will wait for the last async function to finish.
    // The first iteration uses an already resolved Promise
    // so, it will immediately continue.
    await promise;
    const contents = await fs.readFile(file, 'utf8');
    console.log(contents);
  }, Promise.resolve());
}

1
यह पूरी तरह से काम करता है, बहुत बहुत धन्यवाद। क्या आप बता सकते हैं कि यहाँ क्या हो रहा है Promise.resolve()और await promise;?
parrker9

1
यह बहुत अच्छा है। क्या मैं सही सोच रहा हूं कि फाइलों को क्रम में पढ़ा जाएगा और एक बार में ही नहीं?
गोलजेर

1
@ parrker9 Promise.resolve()एक पहले से ही हल की गई Promiseवस्तु लौटाता है , ताकि शुरू करने के लिए reduceएक Promiseहै। को हल करने के लिए श्रृंखला में await promise;अंतिम प्रतीक्षा Promiseकरेंगे। @GollyJer फ़ाइलों को क्रमिक रूप से संसाधित किया जाएगा, एक समय में एक।
टिमोथी ज़ॉर्न

कम करने का बहुत अच्छा उपयोग, टिप्पणी के लिए धन्यवाद! मैं सिर्फ यह निरूपित करूंगा कि टिप्पणियों में उल्लिखित कुछ अन्य विधियों के विपरीत, यह एक समकालिक है, जिसका अर्थ है कि फाइलें एक के बाद एक पढ़ी जाती हैं और समानांतर में नहीं होती हैं (क्योंकि कम फ़ंक्शन का अगला पुनरावृत्ति पिछले पर निर्भर करता है पुनरावृति, यह समकालिक होना चाहिए)।
शा यज़्कोव

1
@ सहाय, आपका मतलब अनुक्रमिक है, समकालिक नहीं। यह अभी भी अतुल्यकालिक है - यदि अन्य चीजें निर्धारित की जाती हैं, तो वे यहां पुनरावृत्तियों के बीच चलेंगे।
तीमुथियुस Zorn

32

पी-यात्रा NPM लागू सरणी यात्रा तरीकों ताकि वे async / इंतजार के साथ एक बहुत सरल तरीके से इस्तेमाल किया जा सकता पर मॉड्यूल।

आपके मामले के साथ एक उदाहरण:

const { forEach } = require('p-iteration');
const fs = require('fs-promise');

(async function printFiles () {
  const files = await getFilePaths();

  await forEach(files, async (file) => {
    const contents = await fs.readFile(file, 'utf8');
    console.log(contents);
  });
})();

1
मुझे यह पसंद है क्योंकि इसमें जेएस के समान ही कार्य / विधियां हैं - मेरे मामले में मुझे इसके someबजाय आवश्यक था forEach। धन्यवाद!
मिकमेकाना

25

यहाँ कुछ forEachAsyncप्रोटोटाइप हैं। नोट आपको awaitउनकी आवश्यकता होगी :

Array.prototype.forEachAsync = async function (fn) {
    for (let t of this) { await fn(t) }
}

Array.prototype.forEachAsyncParallel = async function (fn) {
    await Promise.all(this.map(fn));
}

ध्यान दें कि आप इसे अपने कोड में शामिल कर सकते हैं, आपको इसे उन पुस्तकालयों में शामिल नहीं करना चाहिए जिन्हें आप दूसरों को वितरित करते हैं (अपने ग्लोबल्स को प्रदूषित करने से बचने के लिए)।


1
हालाँकि मैं चीजों को सीधे प्रोटोटाइप में जोड़ने में संकोच कर रहा हूँ, यह एक अच्छा async forEach कार्यान्वयन है
DaniOcean

2
जब तक नाम भविष्य में अद्वितीय है (जैसे मैं उपयोग करूँगा _forEachAsync) यह उचित है। मुझे भी लगता है कि यह सबसे अच्छा जवाब है क्योंकि यह बहुत सारे बॉयलरप्लेट कोड को बचाता है।
मिकमेकाना

1
@estus अन्य लोगों के कोड को प्रदूषित करने से बचने के लिए है। यदि कोड हमारे व्यक्तिगत संगठन का है, और ग्लोबल्स एक अच्छी तरह से पहचानी गई फ़ाइल में हैं ( globals.jsतो अच्छा होगा) हम ग्लोबल्स को जोड़ सकते हैं जैसे हम चाहते हैं।
मिकमेकाना

1
@ मिमिकमेकाना आमतौर पर स्वीकृत बुरी प्रथाओं से बचने के लिए। यह सच है, यह तब तक किया जा सकता है जब तक आप केवल प्रथम-पक्ष कोड का उपयोग करते हैं, जो शायद ही कभी होता है। समस्या यह है कि जब आप थर्ड-पार्टी लिबास का उपयोग करते हैं, तो कोई दूसरा लड़का हो सकता है जो उसी तरह से महसूस करता है और उसी ग्लोबल्स को संशोधित करता है, सिर्फ इसलिए कि यह उस समय एक अच्छा विचार था जब एक लिबर लिखा गया था।
एस्टस फ्लास्क

1
@estus ज़रूर। मैंने यहां (विशेष रूप से उत्पादक नहीं) चर्चा को बचाने के लिए प्रश्न के लिए एक चेतावनी जोड़ी है।
मिकमेकाना

6

@ बर्गी के उत्तर के अलावा , मैं एक तीसरा विकल्प देना चाहूंगा। यह @ बर्गी के 2 के उदाहरण से बहुत मिलता-जुलता है, लेकिन प्रत्येक readFileव्यक्ति की प्रतीक्षा करने के बजाय , आप वादों की एक सरणी बनाते हैं, जिनमें से प्रत्येक का आप अंत में इंतजार करते हैं।

import fs from 'fs-promise';
async function printFiles () {
  const files = await getFilePaths();

  const promises = files.map((file) => fs.readFile(file, 'utf8'))

  const contents = await Promise.all(promises)

  contents.forEach(console.log);
}

ध्यान दें कि फ़ंक्शन को पारित करने की .map()आवश्यकता नहीं है async, क्योंकि fs.readFileवैसे भी एक वादा वस्तु लौटाता है। इसलिए promisesवादा वस्तुओं की एक सरणी है, जिसे भेजा जा सकता है Promise.all()

@ बर्गी के उत्तर में, कंसोल फ़ाइल सामग्री को उसी क्रम में लॉग कर सकते हैं जिस क्रम में वे पढ़ रहे हैं। उदाहरण के लिए यदि वास्तव में एक छोटी फ़ाइल वास्तव में बड़ी फ़ाइल से पहले पढ़ती है, तो इसे पहले लॉग किया जाएगा, भले ही सरणी में बड़ी फ़ाइल के बाद छोटी फ़ाइल आए files। हालाँकि, ऊपर मेरी विधि में, आपको गारंटी है कि कंसोल उसी क्रम में फ़ाइलों को लॉग करेगा जो प्रदान की गई सरणी के रूप में है।


1
मुझे पूरा यकीन है कि आप गलत हैं: मुझे पूरा यकीन है कि आपका तरीका भी फाइलों को क्रम से पढ़ सकता है। हां, यह आउटपुट को सही क्रम में ( await Promise.all) के कारण लॉग करेगा , लेकिन फाइलों को एक अलग क्रम में पढ़ा जा सकता है, जो आपके कथन का खंडन करता है "आपको गारंटी है कि कंसोल फाइलों को उसी क्रम में लॉग इन करेंगे जैसे वे हैं" पढ़ें "।
वेनरिक्स

1
@Venryx आप सही हैं, सुधार के लिए धन्यवाद। मैंने अपना उत्तर अपडेट कर दिया है।
छरवे

5

जब fsवादा किया जाता है तब बर्गी का समाधान अच्छी तरह से काम करता है। आप उपयोग कर सकते हैं bluebird, fs-extraया इसके fs-promiseलिए।

हालाँकि, नोड के मूल fsपरिवाद के लिए समाधान इस प्रकार है:

const result = await Promise.all(filePaths
    .map( async filePath => {
      const fileContents = await getAssetFromCache(filePath, async function() {

        // 1. Wrap with Promise    
        // 2. Return the result of the Promise
        return await new Promise((res, rej) => {
          fs.readFile(filePath, 'utf8', function(err, data) {
            if (data) {
              res(data);
            }
          });
        });
      });

      return fileContents;
    }));

नोट: require('fs') अनिवार्य रूप से 3 तर्क के रूप में कार्य करता है, अन्यथा त्रुटि फेंकता है:

TypeError [ERR_INVALID_CALLBACK]: Callback must be a function

4

काम के ऊपर दोनों समाधान, हालांकि, एंटोनियो कम कोड के साथ काम करता है, यहां बताया गया है कि इसने मुझे अपने डेटाबेस से डेटा को हल करने में मदद की, कई अलग-अलग बच्चों के रिफ से और फिर उन सभी को एक सरणी में धकेल दिया और सभी के बाद एक वादा में इसे हल किया। किया हुआ:

Promise.all(PacksList.map((pack)=>{
    return fireBaseRef.child(pack.folderPath).once('value',(snap)=>{
        snap.forEach( childSnap => {
            const file = childSnap.val()
            file.id = childSnap.key;
            allItems.push( file )
        })
    })
})).then(()=>store.dispatch( actions.allMockupItems(allItems)))

3

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

module.exports = function () {
  var self = this;

  this.each = async (items, fn) => {
    if (items && items.length) {
      await Promise.all(
        items.map(async (item) => {
          await fn(item);
        }));
    }
  };

  this.reduce = async (items, fn, initialValue) => {
    await self.each(
      items, async (item) => {
        initialValue = await fn(initialValue, item);
      });
    return initialValue;
  };
};

अब, यह मानते हुए कि './myAsync.js' पर सहेजा गया है, आप आसन्न फ़ाइल में नीचे के समान कुछ कर सकते हैं:

...
/* your server setup here */
...
var MyAsync = require('./myAsync');
var Cat = require('./models/Cat');
var Doje = require('./models/Doje');
var example = async () => {
  var myAsync = new MyAsync();
  var doje = await Doje.findOne({ name: 'Doje', noises: [] }).save();
  var cleanParams = [];

  // FOR EACH EXAMPLE
  await myAsync.each(['bork', 'concern', 'heck'], 
    async (elem) => {
      if (elem !== 'heck') {
        await doje.update({ $push: { 'noises': elem }});
      }
    });

  var cat = await Cat.findOne({ name: 'Nyan' });

  // REDUCE EXAMPLE
  var friendsOfNyanCat = await myAsync.reduce(cat.friends,
    async (catArray, friendId) => {
      var friend = await Friend.findById(friendId);
      if (friend.name !== 'Long cat') {
        catArray.push(friend.name);
      }
    }, []);
  // Assuming Long Cat was a friend of Nyan Cat...
  assert(friendsOfNyanCat.length === (cat.friends.length - 1));
}

2
मामूली परिशिष्ट, कोशिश / पकड़ ब्लॉक में अपने इंतजार / asyncs लपेटो मत भूलना !!
जे एडवर्ड्स

3

जैसे @ बर्गी की प्रतिक्रिया, लेकिन एक अंतर के साथ।

Promise.all यदि कोई अस्वीकृत हो जाता है तो सभी वादों को खारिज कर देता है।

तो, एक पुनरावर्तन का उपयोग करें।

const readFilesQueue = async (files, index = 0) {
    const contents = await fs.readFile(files[index], 'utf8')
    console.log(contents)

    return files.length <= index
        ? readFilesQueue(files, ++index)
        : files

}

const printFiles async = () => {
    const files = await getFilePaths();
    const printContents = await readFilesQueue(files)

    return printContents
}

printFiles()

पुनश्च

readFilesQueueprintFilesइसके साइड इफेक्ट के कारण * से बाहर है console.log, यह मज़ाक करना, परीक्षण करना और जासूसी करना बेहतर है, इसलिए, यह एक फ़ंक्शन है जो सामग्री (सिडेनोट) लौटाता है, अच्छा नहीं है।

इसलिए, कोड को केवल उसी के द्वारा डिज़ाइन किया जा सकता है: तीन अलग-अलग फ़ंक्शन जो "शुद्ध" ** हैं और कोई साइड इफेक्ट्स पेश नहीं करते हैं, पूरी सूची को संसाधित करते हैं और आसानी से विफल मामलों को संभालने के लिए संशोधित किया जा सकता है।

const files = await getFilesPath()

const printFile = async (file) => {
    const content = await fs.readFile(file, 'utf8')
    console.log(content)
}

const readFiles = async = (files, index = 0) => {
    await printFile(files[index])

    return files.lengh <= index
        ? readFiles(files, ++index)
        : files
}

readFiles(files)

भविष्य संपादित करें / वर्तमान स्थिति

नोड शीर्ष-स्तर की प्रतीक्षा का समर्थन करता है (इसमें अभी तक प्लगइन नहीं है, सामंजस्य स्थापित नहीं किया जा सकता है और सद्भाव के झंडे के माध्यम से सक्षम किया जा सकता है), यह शांत है लेकिन एक समस्या को हल नहीं करता है (रणनीतिक रूप से मैं केवल एलटीएस संस्करणों पर काम करता हूं)। फाइलें कैसे प्राप्त करें?

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

// more complex version with IIFE to a single module
(async (files) => readFiles(await files())(getFilesPath)

ध्यान दें कि शब्दार्थ के कारण परिवर्तनशील का नाम बदल जाता है। आप एक फ़नकार (एक फ़ंक्शन जिसे किसी अन्य फ़ंक्शन द्वारा आमंत्रित किया जा सकता है) पास करते हैं और मेमोरी पर एक पॉइंटर को पुनः प्राप्त करता है जिसमें एप्लिकेशन के तर्क का प्रारंभिक ब्लॉक होता है।

लेकिन, अगर एक मॉड्यूल नहीं है और आपको तर्क को निर्यात करने की आवश्यकता है?

फ़ंक्शन को एक async फ़ंक्शन में लपेटें।

export const readFilesQueue = async () => {
    // ... to code goes here
}

या चर के नाम बदलें, जो भी ...


* साइड इफ़ेक्ट मेनन द्वारा अनुप्रयोग के किसी भी कोलेटेरल प्रभाव को प्रभावित करता है जो आईओ की तरह, स्टेटमेंट / व्यवहार या अनुप्रयोग में खराब होने वाले कीड़े को बदल सकता है।

** "शुद्ध" द्वारा, यह एपोस्ट्रोफ में है क्योंकि फ़ंक्शन यह शुद्ध नहीं है और कोड को शुद्ध संस्करण में परिवर्तित किया जा सकता है, जब कोई कंसोल आउटपुट नहीं होता है, केवल डेटा जोड़तोड़ होता है।

इसके अलावा, शुद्ध होने के लिए, आपको साइड इफेक्ट्स को संभालने वाले मोनाड्स के साथ काम करने की आवश्यकता होगी, जो त्रुटि प्रवण हैं, और उस त्रुटि को एप्लिकेशन के अलग से मानते हैं।


2

एक महत्वपूर्ण चेतावनी है: await + for .. ofविधि और forEach + asyncतरीका वास्तव में अलग-अलग प्रभाव डालता है।

awaitएक असली forलूप के अंदर होने से यह सुनिश्चित हो जाएगा कि सभी async कॉल को एक-एक करके निष्पादित किया जाता है। और जिस forEach + asyncतरह से एक ही समय में सभी वादों को आग लगाएगा, जो तेज है लेकिन कभी-कभी अभिभूत होता है ( यदि आप कुछ डीबी क्वेरी करते हैं या वॉल्यूम प्रतिबंधों के साथ कुछ वेब सेवाओं पर जाते हैं और एक बार में 100,000 कॉल फायर नहीं करना चाहते हैं)।

आप उपयोग नहीं कर सकते reduce + promise(कम सुरुचिपूर्ण) यदि आप उपयोग नहीं करते हैं async/awaitऔर सुनिश्चित करना चाहते हैं कि फाइलें एक के बाद एक पढ़ी जाएं ।

files.reduce((lastPromise, file) => 
 lastPromise.then(() => 
   fs.readFile(file, 'utf8')
 ), Promise.resolve()
)

या आप मदद के लिए एक forEachAsync बना सकते हैं, लेकिन मूल रूप से अंतर्निहित लूप के लिए इसका उपयोग करते हैं।

Array.prototype.forEachAsync = async function(cb){
    for(let x of this){
        await cb(x);
    }
}

Array.prototype और object.prototype पर जावास्क्रिप्ट में विधि को कैसे परिभाषित करें , इस पर एक नज़र डालें ताकि यह लूप में दिखाई न दे । इसके अलावा, आपको शायद मूल के रूप में समान पुनरावृत्ति का उपयोग करना चाहिए forEach- पुनरावृत्ति पर निर्भर होने के बजाय सूचकांकों तक पहुंच - और सूचकांक को कॉलबैक में पास करें।
बरगी

आप Array.prototype.reduceइस तरह से उपयोग कर सकते हैं जो एक async फ़ंक्शन का उपयोग करता है। मैंने अपने उत्तर में एक उदाहरण दिखाया है: stackoverflow.com/a/49499491/2537258
टिमोथी जोर्न

2

टास्क, फ्यूचरिज़ और ट्रैवर्सेबल लिस्ट का उपयोग करके, आप बस कर सकते हैं

async function printFiles() {
  const files = await getFiles();

  List(files).traverse( Task.of, f => readFile( f, 'utf-8'))
    .fork( console.error, console.log)
}

यहाँ बताया गया है कि आप इसे कैसे सेट करेंगे

import fs from 'fs';
import { futurize } from 'futurize';
import Task from 'data.task';
import { List } from 'immutable-ext';

const future = futurizeP(Task)
const readFile = future(fs.readFile)

वांछित कोड होगा संरचित करने का दूसरा तरीका

const printFiles = files => 
  List(files).traverse( Task.of, fn => readFile( fn, 'utf-8'))
    .fork( console.error, console.log)

या शायद और भी अधिक कार्यात्मक रूप से उन्मुख

// 90% of encodings are utf-8, making that use case super easy is prudent

// handy-library.js
export const readFile = f =>
  future(fs.readFile)( f, 'utf-8' )

export const arrayToTaskList = list => taskFn => 
  List(files).traverse( Task.of, taskFn ) 

export const readFiles = files =>
  arrayToTaskList( files, readFile )

export const printFiles = files => 
  readFiles(files).fork( console.error, console.log)

फिर मूल कार्य से

async function main() {
  /* awesome code with side-effects before */
  printFiles( await getFiles() );
  /* awesome code with side-effects after */
}

यदि आप वास्तव में एन्कोडिंग में अधिक लचीलापन चाहते हैं, तो आप ऐसा कर सकते हैं (मज़े के लिए, मैं प्रस्तावित पाइप फॉरवर्ड ऑपरेटर का उपयोग कर रहा हूं )

import { curry, flip } from 'ramda'

export const readFile = fs.readFile 
  |> future,
  |> curry,
  |> flip

export const readFileUtf8 = readFile('utf-8')

PS - मैंने कंसोल पर इस कोड की कोशिश नहीं की, हो सकता है कि कुछ टाइपोस हो ... "सीधे फ्रीस्टाइल, गुंबद के ऊपर से!" जैसा कि 90 के दशक के बच्चे कहेंगे। :-P


2

वर्तमान में Array.forEach प्रोटोटाइप प्रॉपर्टी, Async ऑपरेशंस का समर्थन नहीं करती है, लेकिन हम अपनी जरूरतों को पूरा करने के लिए अपना स्वयं का पाली-फिल बना सकते हैं।

// Example of asyncForEach Array poly-fill for NodeJs
// file: asyncForEach.js
// Define asynForEach function 
async function asyncForEach(iteratorFunction){
  let indexer = 0
  for(let data of this){
    await iteratorFunction(data, indexer)
    indexer++
  }
}
// Append it as an Array prototype property
Array.prototype.asyncForEach = asyncForEach
module.exports = {Array}

और बस! अब आपके पास किसी भी सरणियों पर एक async फोरैच विधि उपलब्ध है जो इन ऑपरेशनों के बाद परिभाषित होती है।

आइए इसका परीक्षण करते हैं ...

// Nodejs style
// file: someOtherFile.js

const readline = require('readline')
Array = require('./asyncForEach').Array
const log = console.log

// Create a stream interface
function createReader(options={prompt: '>'}){
  return readline.createInterface({
    input: process.stdin
    ,output: process.stdout
    ,prompt: options.prompt !== undefined ? options.prompt : '>'
  })
}
// Create a cli stream reader
async function getUserIn(question, options={prompt:'>'}){
  log(question)
  let reader = createReader(options)
  return new Promise((res)=>{
    reader.on('line', (answer)=>{
      process.stdout.cursorTo(0, 0)
      process.stdout.clearScreenDown()
      reader.close()
      res(answer)
    })
  })
}

let questions = [
  `What's your name`
  ,`What's your favorite programming language`
  ,`What's your favorite async function`
]
let responses = {}

async function getResponses(){
// Notice we have to prepend await before calling the async Array function
// in order for it to function as expected
  await questions.asyncForEach(async function(question, index){
    let answer = await getUserIn(question)
    responses[question] = answer
  })
}

async function main(){
  await getResponses()
  log(responses)
}
main()
// Should prompt user for an answer to each question and then 
// log each question and answer as an object to the terminal

हम मानचित्र जैसे कुछ अन्य सरणी कार्यों के लिए भी ऐसा ही कर सकते हैं ...

async function asyncMap(iteratorFunction){
  let newMap = []
  let indexer = 0
  for(let data of this){
    newMap[indexer] = await iteratorFunction(data, indexer, this)
    indexer++
  }
  return newMap
}

Array.prototype.asyncMap = asyncMap

... और इसी तरह :)

ध्यान देने योग्य कुछ बातें:

  • आपका iteratorFunction एक async फ़ंक्शन या वादा होना चाहिए
  • इससे पहले बनाए गए किसी भी सरण Array.prototype.<yourAsyncFunc> = <yourAsyncFunc>में यह सुविधा उपलब्ध नहीं होगी

2

सिर्फ मूल उत्तर से जोड़कर

  • मूल उत्तर में समानांतर रीडिंग सिंटैक्स कभी-कभी भ्रमित और पढ़ने में मुश्किल होता है, शायद हम इसे एक अलग दृष्टिकोण में लिख सकते हैं
async function printFiles() {
  const files = await getFilePaths();
  const fileReadPromises = [];

  const readAndLogFile = async filePath => {
    const contents = await fs.readFile(file, "utf8");
    console.log(contents);
    return contents;
  };

  files.forEach(file => {
    fileReadPromises.push(readAndLogFile(file));
  });

  await Promise.all(fileReadPromises);
}
  • अनुक्रमिक संचालन के लिए, न केवल ... के लिए , लूप के लिए सामान्य भी काम करेगा
async function printFiles() {
  const files = await getFilePaths();

  for (let i = 0; i < files.length; i++) {
    const file = files[i];
    const contents = await fs.readFile(file, "utf8");
    console.log(contents);
  }
}

1

आज मैं इसके लिए कई समाधानों पर आया हूं। एस्कॉन वेट चलाने के लिए आगे लूप में कार्य करता है। चारों ओर आवरण बनाकर हम ऐसा कर सकते हैं।

देशी के लिए यह आंतरिक रूप से कैसे काम करता है, इस पर अधिक विस्तृत विवरण और क्यों यह एक async फ़ंक्शन कॉल करने में सक्षम नहीं है और विभिन्न तरीकों पर अन्य विवरण यहां लिंक में दिए गए हैं

कई तरीके जिनके माध्यम से यह किया जा सकता है और वे इस प्रकार हैं,

विधि 1: आवरण का उपयोग करना।

await (()=>{
     return new Promise((resolve,reject)=>{
       items.forEach(async (item,index)=>{
           try{
               await someAPICall();
           } catch(e) {
              console.log(e)
           }
           count++;
           if(index === items.length-1){
             resolve('Done')
           }
         });
     });
    })();

विधि 2: Array.prototype के जेनेरिक फ़ंक्शन के समान उपयोग करना

Array.prototype.forEachAsync.js

if(!Array.prototype.forEachAsync) {
    Array.prototype.forEachAsync = function (fn){
      return new Promise((resolve,reject)=>{
        this.forEach(async(item,index,array)=>{
            await fn(item,index,array);
            if(index === array.length-1){
                resolve('done');
            }
        })
      });
    };
  }

उपयोग:

require('./Array.prototype.forEachAsync');

let count = 0;

let hello = async (items) => {

// Method 1 - Using the Array.prototype.forEach 

    await items.forEachAsync(async () => {
         try{
               await someAPICall();
           } catch(e) {
              console.log(e)
           }
        count++;
    });

    console.log("count = " + count);
}

someAPICall = () => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("done") // or reject('error')
        }, 100);
    })
}

hello(['', '', '', '']); // hello([]) empty array is also be handled by default

विधि 3:

Promise.all का उपयोग करना

  await Promise.all(items.map(async (item) => {
        await someAPICall();
        count++;
    }));

    console.log("count = " + count);

विधि 4: लूप के लिए पारंपरिक या लूप के लिए आधुनिक

// Method 4 - using for loop directly

// 1. Using the modern for(.. in..) loop
   for(item in items){

        await someAPICall();
        count++;
    }

//2. Using the traditional for loop 

    for(let i=0;i<items.length;i++){

        await someAPICall();
        count++;
    }


    console.log("count = " + count);

आपके तरीके 1 और 2 केवल गलत कार्यान्वयन Promise.allहैं, जिनका उपयोग किया जाना चाहिए था - वे कई किनारे के मामलों को ध्यान में नहीं रखते हैं।
बर्गी

@Bergi: मान्य टिप्पणियों के लिए धन्यवाद, क्या आप मुझे बताएंगे कि विधि 1 और 2 गलत क्यों हैं। यह उद्देश्य भी पूरा करता है। यह बहुत अच्छा काम करता है। यह कहना है कि ये सभी तरीके संभव हैं, स्थिति के आधार पर कोई एक चुनने का फैसला कर सकता है। मेरे पास इसके लिए एक उदाहरण है।
प्रणवकांड्रो

यह खाली सरणियों पर विफल रहता है, इसमें किसी भी त्रुटि से निपटने, और शायद अधिक समस्याएं नहीं हैं। पहिया को सुदृढ़ मत करो। बस उपयोग करें Promise.all
Bergi

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

नहीं, ऐसी कोई भी स्थिति नहीं है जहाँ Promise.allसंभव नहीं है लेकिन async/ awaitहै। और नहीं, forEachबिल्कुल किसी भी त्रुटि त्रुटियों को संभाल नहीं करता है।
बरगी

1

यह समाधान मेमोरी-ऑप्टिमाइज़्ड भी है ताकि आप इसे 10,000 डेटा आइटम और अनुरोधों पर चला सकें। यहां कुछ अन्य समाधान बड़े डेटा सेट पर सर्वर को क्रैश कर देंगे।

टाइपस्क्रिप्ट में:

export async function asyncForEach<T>(array: Array<T>, callback: (item: T, index: number) => void) {
        for (let index = 0; index < array.length; index++) {
            await callback(array[index], index);
        }
    }

कैसे इस्तेमाल करे?

await asyncForEach(receipts, async (eachItem) => {
    await ...
})

1

आप उपयोग कर सकते हैं Array.prototype.forEach, लेकिन async / प्रतीक्षा इतनी संगत नहीं है। ऐसा इसलिए है क्योंकि एक async कॉलबैक से दिया गया वादा हल होने की उम्मीद Array.prototype.forEachकरता है , लेकिन इसके कॉलबैक के निष्पादन से किसी भी वादे को हल नहीं करता है। तो फिर, आप forEach का उपयोग कर सकते हैं, लेकिन आपको खुद ही वादा समाधान संभालना होगा।

यहां श्रृंखला का उपयोग करके प्रत्येक फ़ाइल को पढ़ने और प्रिंट करने का एक तरीका है Array.prototype.forEach

async function printFilesInSeries () {
  const files = await getFilePaths()

  let promiseChain = Promise.resolve()
  files.forEach((file) => {
    promiseChain = promiseChain.then(() => {
      fs.readFile(file, 'utf8').then((contents) => {
        console.log(contents)
      })
    })
  })
  await promiseChain
}

Array.prototype.forEachसमानांतर में फ़ाइलों की सामग्री को प्रिंट करने का एक तरीका (अभी भी उपयोग कर रहा है ) है

async function printFilesInParallel () {
  const files = await getFilePaths()

  const promises = []
  files.forEach((file) => {
    promises.push(
      fs.readFile(file, 'utf8').then((contents) => {
        console.log(contents)
      })
    )
  })
  await Promise.all(promises)
}

पहला सिनारियो छोरों के लिए आदर्श है जिसे
सीरी

0

एंटोनियो वाल्स के समान p-iteration, एक वैकल्पिक एनपीएम मॉड्यूल है async-af:

const AsyncAF = require('async-af');
const fs = require('fs-promise');

function printFiles() {
  // since AsyncAF accepts promises or non-promises, there's no need to await here
  const files = getFilePaths();

  AsyncAF(files).forEach(async file => {
    const contents = await fs.readFile(file, 'utf8');
    console.log(contents);
  });
}

printFiles();

वैकल्पिक रूप से, async-afएक स्थिर विधि (log / logAF) है जो वादों के परिणामों को लॉग करती है:

const AsyncAF = require('async-af');
const fs = require('fs-promise');

function printFiles() {
  const files = getFilePaths();

  AsyncAF(files).forEach(file => {
    AsyncAF.log(fs.readFile(file, 'utf8'));
  });
}

printFiles();

हालाँकि, लाइब्रेरी का मुख्य लाभ यह है कि आप कुछ करने के लिए अतुल्यकालिक तरीकों की श्रृंखला बना सकते हैं:

const aaf = require('async-af');
const fs = require('fs-promise');

const printFiles = () => aaf(getFilePaths())
  .map(file => fs.readFile(file, 'utf8'))
  .forEach(file => aaf.log(file));

printFiles();

async-af


0

यह देखने के लिए कि यह कैसे गलत हो सकता है, विधि के अंत में कंसोल.लॉग प्रिंट करें।

चीजें जो सामान्य रूप से गलत हो सकती हैं:

  • मनमाना आदेश।
  • PrintFiles फाइलों को प्रिंट करने से पहले पूरा कर सकते हैं।
  • घटिया प्रदर्शन।

ये हमेशा गलत नहीं होते हैं, लेकिन अक्सर मानक उपयोग के मामलों में होते हैं।

आम तौर पर, forEach का उपयोग करने के परिणामस्वरूप सभी अंतिम होंगे। यह फ़ंक्शन के लिए प्रतीक्षा किए बिना प्रत्येक फ़ंक्शन को कॉल करेगा जिसका अर्थ है कि यह सभी फ़ंक्शन को शुरू करने के लिए कहता है फिर फ़ंक्शंस के समाप्त होने की प्रतीक्षा किए बिना समाप्त होता है।

import fs from 'fs-promise'

async function printFiles () {
  const files = (await getFilePaths()).map(file => fs.readFile(file, 'utf8'))

  for(const file of files)
    console.log(await file)
}

printFiles()

यह देशी जेएस में एक उदाहरण है जो आदेश को संरक्षित करेगा, फ़ंक्शन को समय से पहले लौटने से रोकेगा और सिद्धांत में इष्टतम प्रदर्शन को बनाए रखेगा।

यह करेगा:

  • फ़ाइल के सभी समानांतर में होने के लिए पढ़ता है।
  • प्रतीक्षा करने के वादे के लिए फ़ाइल नाम को मैप करने के लिए मानचित्र के उपयोग के माध्यम से आदेश को संरक्षित करें।
  • सरणी द्वारा परिभाषित क्रम में प्रत्येक वादे की प्रतीक्षा करें।

इस समाधान के साथ पहली फ़ाइल दिखाई जाएगी जैसे ही यह उपलब्ध होगी बिना दूसरों की प्रतीक्षा किए पहले उपलब्ध होने की।

यह दूसरी फ़ाइल पढ़ने से पहले शुरू होने से पहले समाप्त होने की प्रतीक्षा करने के बजाय एक ही समय में सभी फ़ाइलों को लोड कर रहा होगा।

इसका केवल एक ही कारण है और मूल संस्करण यह है कि यदि एक ही बार में कई रीडर्स शुरू किए जाते हैं तो एक बार में अधिक त्रुटियां होने के कारण त्रुटियों को संभालना अधिक कठिन होता है।

ऐसे संस्करणों के साथ, जो एक समय में एक फ़ाइल पढ़ते हैं, फिर किसी भी अधिक फ़ाइलों को पढ़ने का प्रयास किए बिना समय बर्बाद किए बिना असफल हो जाएंगे। यहां तक ​​कि एक विस्तृत रद्दीकरण प्रणाली के साथ यह पहली फ़ाइल पर असफल होने से बचने के लिए कठिन हो सकता है, लेकिन पहले से ही अन्य फ़ाइलों के अधिकांश को पढ़ना।

प्रदर्शन हमेशा अनुमानित नहीं होता है। जबकि कई सिस्टम समानांतर फाइल रीडिंग के साथ तेज़ होंगे, कुछ अनुक्रमिक पसंद करेंगे। कुछ गतिशील हैं और लोड के तहत बदलाव कर सकते हैं, अनुकूलन जो विलंबता प्रदान करते हैं वे हमेशा भारी विवाद के तहत अच्छे थ्रूपुट का उत्पादन नहीं करते हैं।

उस उदाहरण में कोई त्रुटि हैंडलिंग भी नहीं है। अगर कुछ की आवश्यकता होती है, तो उन्हें या तो सभी को सफलतापूर्वक दिखाया जाना चाहिए या बिल्कुल नहीं।

प्रत्येक चरण में कंसोल.लॉग के साथ गहराई से प्रयोग करने की सिफारिश की जाती है और नकली फ़ाइल रीड सॉल्यूशंस (इसके बजाय यादृच्छिक विलंब)। हालांकि कई समाधान सरल मामलों में भी ऐसा ही करते हैं, सभी में सूक्ष्म अंतर होते हैं जो निचोड़ने के लिए कुछ अतिरिक्त जांच करते हैं।

समाधानों के बीच अंतर बताने में मदद करने के लिए इस मॉक का उपयोग करें:

(async () => {
  const start = +new Date();
  const mock = () => {
    return {
      fs: {readFile: file => new Promise((resolve, reject) => {
        // Instead of this just make three files and try each timing arrangement.
        // IE, all same, [100, 200, 300], [300, 200, 100], [100, 300, 200], etc.
        const time = Math.round(100 + Math.random() * 4900);
        console.log(`Read of ${file} started at ${new Date() - start} and will take ${time}ms.`)
        setTimeout(() => {
          // Bonus material here if random reject instead.
          console.log(`Read of ${file} finished, resolving promise at ${new Date() - start}.`);
          resolve(file);
        }, time);
      })},
      console: {log: file => console.log(`Console Log of ${file} finished at ${new Date() - start}.`)},
      getFilePaths: () => ['A', 'B', 'C', 'D', 'E']
    };
  };

  const printFiles = (({fs, console, getFilePaths}) => {
    return async function() {
      const files = (await getFilePaths()).map(file => fs.readFile(file, 'utf8'));

      for(const file of files)
        console.log(await file);
    };
  })(mock());

  console.log(`Running at ${new Date() - start}`);
  await printFiles();
  console.log(`Finished running at ${new Date() - start}`);
})();

-3

मैं अच्छी तरह से परीक्षण किया (प्रति सप्ताह लाखों डाउनलोड) pify और async मॉड्यूल का उपयोग करेगा। यदि आप async मॉड्यूल से अपरिचित हैं, तो मैं अत्यधिक सलाह देता हूं कि आप इसके डॉक्स देखें । मैंने देखा है कि कई देवता समय बर्बाद कर रहे हैं अपने तरीके, या बदतर, मुश्किल से बनाए रखने के लिए async कोड जब उच्च क्रम async तरीकों कोड को सरल होगा।

const async = require('async')
const fs = require('fs-promise')
const pify = require('pify')

async function getFilePaths() {
    return Promise.resolve([
        './package.json',
        './package-lock.json',
    ]);
}

async function printFiles () {
  const files = await getFilePaths()

  await pify(async.eachSeries)(files, async (file) => {  // <-- run in series
  // await pify(async.each)(files, async (file) => {  // <-- run in parallel
    const contents = await fs.readFile(file, 'utf8')
    console.log(contents)
  })
  console.log('HAMBONE')
}

printFiles().then(() => {
    console.log('HAMBUNNY')
})
// ORDER OF LOGS:
// package.json contents
// package-lock.json contents
// HAMBONE
// HAMBUNNY
```


यह गलत दिशा में एक कदम है। यहाँ एक मैपिंग गाइड है जिसे मैंने आधुनिक जेएस युग में कॉलबैक नरक में फंसे लोगों की मदद के लिए बनाया था: github.com/jmjpro/async-package-to-async-await/blob/master/…
jbustamovej

जैसा कि आप यहाँ देख सकते हैं , मैं async / lib का उपयोग करने में रुचि रखता हूँ और async लिब की बजाय प्रतीक्षा कर रहा हूँ। अभी, मुझे लगता है कि प्रत्येक का समय और स्थान है। मुझे यकीन नहीं है कि async lib == "कॉलबैक नरक" और async / प्रतीक्षा == "आधुनिक JS युग"। imo, जब async lib> async / इंतजार: 1. जटिल प्रवाह (जैसे, कतार, कार्गो, यहां तक ​​कि ऑटो जब चीजें जटिल हो जाती हैं) 2. संगामिति 3. सहायक सरणियों / वस्तुओं / पुनरावृत्तियों 4. गलत हैंडलिंग
Zachary रयान स्मिथ
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.