Async का उपयोग करना / forEach लूप के साथ प्रतीक्षा करना
वहाँ का उपयोग कर के साथ किसी भी मुद्दे हैं 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
, इसलिए मैं सिर्फ यह पूछना चाहता था कि क्या इसके लिए कोई समस्या थी।
जवाब
यकीन है कि कोड काम करता है, लेकिन मुझे पूरा यकीन है कि यह वह नहीं करता है जो आप इसे करने की उम्मीद करते हैं। यह सिर्फ कई अतुल्यकालिक कॉल बंद कर देता है, लेकिन इसके 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)
}));
}
ES2018 के साथ, आप उपरोक्त सभी उत्तरों को सरल बनाने में सक्षम हैं:
async function printFiles () {
const files = await getFilePaths()
for await (const contents of fs.readFile(file, 'utf8')) {
console.log(contents)
}
}
कल्पना देखें: प्रस्ताव-एसिंक्स-पुनरावृत्ति
2018-09-10: इस उत्तर पर हाल ही में बहुत ध्यान दिया जा रहा है, कृपया अतुल्यकालिक पुनरावृत्ति के बारे में अधिक जानकारी के लिए एक्सल रौशमेयर के ब्लॉग पोस्ट को देखें: ES2018: अतुल्यकालिक पुनरावृत्ति
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());
}
पी-यात्रा 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);
});
})();
यहाँ कुछ 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
। हालाँकि, ऊपर मेरी विधि में, आपको गारंटी है कि कंसोल उसी क्रम में फ़ाइलों को लॉग करेगा जो प्रदान की गई सरणी में है।
जब 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
काम के ऊपर दोनों समाधान, हालांकि, एंटोनियो कम कोड के साथ काम करता है, यहां बताया गया है कि इसने मुझे अपने डेटाबेस से डेटा को हल करने में मदद की, कई अलग-अलग बच्चों के रिफ से और फिर उन सभी को एक सरणी में धकेल दिया और सभी के बाद एक वादा में इसे हल किया किया हुआ:
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)))
यह एक फ़ाइल में कुछ तरीकों को पॉप करने के लिए बहुत दर्दनाक है जो क्रमबद्ध क्रम में अतुल्यकालिक डेटा को संभालेंगे और अपने कोड को अधिक पारंपरिक स्वाद देंगे। उदाहरण के लिए:
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));
}
यह समाधान मेमोरी-ऑप्टिमाइज़्ड भी है ताकि आप इसे 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 ...
})
एक महत्वपूर्ण चेतावनी है: 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);
}
}
सिर्फ मूल उत्तर से जोड़कर
- मूल उत्तर में समानांतर रीडिंग सिंटैक्स कभी-कभी भ्रमित और पढ़ने में मुश्किल होता है, शायद हम इसे एक अलग दृष्टिकोण में लिख सकते हैं
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);
}
}
@ बर्गी की प्रतिक्रिया की तरह, लेकिन एक अंतर के साथ।
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()
पुनश्च
readFilesQueue
printFiles
साइड इफेक्ट के कारण से बाहर है * द्वारा शुरू की 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
}
या चर के नाम बदलो, जो भी ...
*
साइड इफ़ेक्ट मेनन द्वारा एप्लिकेशन के किसी भी कोलेटेरल प्रभाव को प्रभावित करता है जो आईओ की तरह, एप्लिकेशन में स्टेट / व्यवहार या इनग्रॉस बग को बदल सकता है।
**
"शुद्ध" द्वारा, यह एपोस्ट्रोफ़ में है क्योंकि फ़ंक्शन यह शुद्ध नहीं है और कोड को शुद्ध संस्करण में परिवर्तित किया जा सकता है, जब कोई कंसोल आउटपुट नहीं होता है, केवल डेटा जोड़तोड़ होता है।
इसके अलावा, शुद्ध होने के लिए, आपको साइड इफेक्ट्स को संभालने वाले मोनाड्स के साथ काम करने की आवश्यकता होगी, जो त्रुटि प्रवण हैं, और उस त्रुटि को अनुप्रयोग के अलग-अलग मानते हैं।
टास्क, फ्यूचरिज़ और ट्रैवर्सेबल लिस्ट का उपयोग करके, आप बस कर सकते हैं
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
वर्तमान में Array.forEach प्रोटोटाइप प्रॉपर्टी, एसिंक्स ऑपरेशंस का समर्थन नहीं करती है, लेकिन हम अपनी जरूरतों को पूरा करने के लिए अपना पॉली-फिल बना सकते हैं।
// 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
... और इसी तरह :)
ध्यान देने योग्य कुछ बातें:
- आपका इटेरिफ़ंक्शन एक एसिंक्स फ़ंक्शन या वादा होना चाहिए
- इससे पहले बनाए गए किसी भी सरण
Array.prototype.<yourAsyncFunc> = <yourAsyncFunc>
में यह सुविधा उपलब्ध नहीं होगी
आज मैं इसके लिए कई समाधानों पर आया हूं। एस्कॉन वेट चलाने के लिए लूप में काम करता है। चारों ओर आवरण बनाकर हम ऐसा कर सकते हैं।
कई तरीके जिनके माध्यम से यह किया जा सकता है और वे निम्नानुसार हैं,
विधि 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);
आप उपयोग कर सकते हैं 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)
}
यह देखने के लिए कि यह कैसे गलत हो सकता है, विधि के अंत में कंसोल.लॉग प्रिंट करें।
चीजें जो सामान्य रूप से गलत हो सकती हैं:
- मनमाना आदेश।
- 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}`);
})();
एंटोनियो वाल्स के समान 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();