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




node.js promise (10)

वहाँ किसी भी मुद्दे के साथ async / 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 का उपयोग करने वाले नहीं हैं, इसलिए मैं सिर्फ यह पूछना चाहता था कि क्या इसके साथ कोई समस्या थी।


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

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

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

कल्पना देखें: https://github.com/tc39/proposal-async-iteration

2018-09-10: इस जवाब पर हाल ही में बहुत ध्यान दिया जा रहा है, कृपया अतुल्यकालिक पुनरावृत्ति के बारे में अधिक जानकारी के लिए एक्सल रौशमेयर के ब्लॉग पोस्ट को देखें: http://2ality.com/2016/10/asynchronous-iteration.html


Npm पर p-iteration मॉड्यूल Array पुनरावृत्ति विधियों को कार्यान्वित करता है, इसलिए उन्हें 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);
  });
})();

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

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

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

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

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

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

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

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


मैं अच्छी तरह से परीक्षण किया (प्रति सप्ताह लाखों डाउनलोड) 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
```


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

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

यहाँ कुछ 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));
}

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


@ बर्गी के जवाब के अलावा, मैं एक तीसरा विकल्प देना चाहूंगा। यह @ बर्गी के 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 सरणी में बड़ी फ़ाइल के बाद छोटी फ़ाइल आए। हालांकि, ऊपर मेरी विधि में, आपको गारंटी है कि कंसोल उसी क्रम में फ़ाइलों को लॉग करेंगे जैसे वे पढ़े जाते हैं।


Array.prototype.map के साथ Array.prototype.map बजाय (जो उस क्रम की गारंटी नहीं देता है जिसमें Promise s हल किए गए हैं), मैं Array.prototype.reduce उपयोग करता 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());
}




ecmascript-2017