Menggunakan async / await dengan perulangan forEach

Jun 02 2016

Apakah ada masalah dengan menggunakan async/ awaitdalam satu forEachloop? Saya mencoba untuk mengulang melalui array file dan awaitisi setiap file.

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

Kode ini berfungsi, tetapi apakah ada yang salah dengan ini? Saya meminta seseorang memberi tahu saya bahwa Anda tidak seharusnya menggunakan async/ awaitdalam fungsi urutan yang lebih tinggi seperti ini, jadi saya hanya ingin bertanya apakah ada masalah dengan ini.

Jawaban

2680 Bergi Jun 02 2016 at 02:02

Tentu kodenya berfungsi, tapi saya cukup yakin itu tidak melakukan apa yang Anda harapkan. Ini hanya mengaktifkan beberapa panggilan asinkron, tetapi printFilesfungsinya segera kembali setelah itu.

Membaca secara berurutan

Jika Anda ingin membaca file secara berurutan, Anda tidak dapat menggunakannyaforEach . Cukup gunakan for … ofloop modern sebagai gantinya, yang awaitakan berfungsi seperti yang diharapkan:

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

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

Membaca secara paralel

Jika Anda ingin membaca file secara paralel, Anda tidak dapat menggunakannyaforEach . Setiap asyncpanggilan fungsi callback memang mengembalikan sebuah promise, tetapi Anda membuangnya alih-alih menunggunya. Cukup gunakan mapsebagai gantinya, dan Anda bisa menunggu berbagai janji yang akan Anda dapatkan 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)
  }));
}
274 FranciscoMateo Jun 15 2018 at 18:17

Dengan ES2018, Anda dapat menyederhanakan semua jawaban di atas menjadi:

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

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

Lihat spesifikasi: proposal-async-iteration


2018-09-10: Jawaban ini mendapatkan banyak perhatian baru-baru ini, silakan lihat postingan blog Axel Rauschmayer untuk informasi lebih lanjut tentang asynchronous iteration: ES2018: asynchronous iteration

81 TimothyZorn Mar 27 2018 at 02:48

Alih-alih Promise.alldalam hubungannya dengan Array.prototype.map(yang tidak menjamin urutan Promisepenyelesaian), saya menggunakan Array.prototype.reduce, dimulai dengan diselesaikan 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());
}
35 AntonioVal Jul 10 2017 at 15:15

The p-iterasi modul alat NPM metode Array iterasi sehingga mereka dapat digunakan dalam cara yang sangat sederhana dengan async / Tunggulah.

Contoh kasus Anda:

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);
  });
})();
32 Matt Mar 22 2018 at 22:11

Berikut beberapa forEachAsyncprototipe. Perhatikan Anda akan membutuhkannya 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));
}

Perhatikan meskipun Anda dapat memasukkan ini ke dalam kode Anda sendiri, Anda tidak boleh menyertakan ini di perpustakaan yang Anda distribusikan kepada orang lain (untuk menghindari pencemaran global mereka).

9 chharvey Feb 23 2018 at 07:47

Selain jawaban @ Bergi , saya ingin menawarkan alternatif ketiga. Ini sangat mirip dengan contoh ke-2 @ Bergi, tetapi alih-alih menunggu satu readFileper satu , Anda membuat serangkaian janji, yang masing-masing Anda tunggu di akhir.

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

Perhatikan bahwa fungsi yang diteruskan ke .map()tidak perlu async, karena tetap fs.readFilemengembalikan objek Promise. Karenanya promisesadalah larik objek Promise, yang dapat dikirim ke Promise.all().

Dalam jawaban @ Bergi, konsol dapat mencatat konten file dalam urutan pembacaannya. Misalnya jika file yang sangat kecil selesai dibaca sebelum file yang sangat besar, itu akan dicatat terlebih dahulu, bahkan jika file kecil muncul setelah file besar dalam fileslarik. Namun, dalam metode saya di atas, Anda dijamin konsol akan mencatat file dalam urutan yang sama seperti array yang disediakan.

7 master_dodo May 27 2019 at 05:08

Solusi Bergi bekerja dengan baik bila fsdidasarkan pada janji. Anda bisa menggunakan bluebird, fs-extraatau fs-promiseuntuk ini.

Namun, solusi untukfs perpustakaan asli node adalah sebagai berikut:

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

Catatan: secara require('fs') wajib mengambil fungsi sebagai argumen ke-3, jika tidak, akan muncul kesalahan:

TypeError [ERR_INVALID_CALLBACK]: Callback must be a function
6 HoomanAskari Aug 26 2017 at 17:47

Kedua solusi di atas berfungsi, namun, Antonio melakukan pekerjaan dengan lebih sedikit kode, berikut adalah cara membantu saya menyelesaikan data dari database saya, dari beberapa referensi anak yang berbeda dan kemudian mendorong semuanya ke dalam array dan menyelesaikannya dengan janji. selesai:

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)))
5 JayEdwards Sep 23 2017 at 06:03

Sangat mudah untuk memunculkan beberapa metode dalam file yang akan menangani data asinkron dalam urutan serial dan memberikan rasa yang lebih konvensional pada kode Anda. Sebagai contoh:

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

sekarang, dengan asumsi itu disimpan di './myAsync.js' Anda dapat melakukan sesuatu yang mirip dengan di bawah ini dalam file yang berdekatan:

...
/* 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));
}
5 OliverDixon Apr 17 2020 at 00:18

Solusi ini juga dioptimalkan untuk memori sehingga Anda dapat menjalankannya pada 10.000 item data dan permintaan. Beberapa solusi lain di sini akan merusak server pada kumpulan data yang besar.

Di TypeScript:

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

Cara Penggunaan?

await asyncForEach(receipts, async (eachItem) => {
    await ...
})
4 LeOn-HanLi Sep 25 2017 at 03:00

Satu peringatan penting adalah: await + for .. ofMetode dan forEach + asynccaranya sebenarnya memiliki pengaruh yang berbeda.

Memiliki awaitdi dalam forloop nyata akan memastikan semua panggilan async dijalankan satu per satu. Dan forEach + asynccara tersebut akan menjalankan semua janji pada saat yang sama, yang lebih cepat tetapi terkadang kewalahan ( jika Anda melakukan kueri DB atau mengunjungi beberapa layanan web dengan batasan volume dan tidak ingin mengaktifkan 100.000 panggilan sekaligus).

Anda juga dapat menggunakan reduce + promise(kurang elegan) jika Anda tidak menggunakan async/awaitdan ingin memastikan file dibaca satu demi satu .

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

Atau Anda dapat membuat forEachAsync untuk membantu tetapi pada dasarnya menggunakan yang sama untuk loop yang mendasari.

Array.prototype.forEachAsync = async function(cb){
    for(let x of this){
        await cb(x);
    }
}
4 gsaandy Dec 01 2019 at 23:59

Hanya menambahkan jawaban asli

  • Sintaks pembacaan paralel pada jawaban asli terkadang membingungkan dan sulit dibaca, mungkin kita dapat menuliskannya dengan pendekatan yang berbeda
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);
}

  • Untuk operasi sekuensial, tidak hanya untuk ... of , loop for normal juga akan berfungsi
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);
  }
}

4 lukaswilkeer Dec 21 2019 at 08:11

Suka tanggapan @ Bergi, tapi dengan satu perbedaan.

Promise.all menolak semua janji jika ada yang ditolak.

Jadi, gunakan rekursi.

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

PS

readFilesQueuedi luar printFilessebab akibat efek samping * yang diperkenalkan oleh console.log, lebih baik mengejek, menguji, dan atau memata-matai jadi, tidak keren memiliki fungsi yang mengembalikan konten (sidenote).

Oleh karena itu, kode hanya dapat dirancang dengan: tiga fungsi terpisah yang "murni" ** dan tidak menimbulkan efek samping, memproses seluruh daftar dan dapat dengan mudah dimodifikasi untuk menangani kasus yang gagal.

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)

Pengeditan masa depan / status saat ini

Node mendukung menunggu tingkat atas (ini belum memiliki plugin, tidak akan memiliki dan dapat diaktifkan melalui bendera harmoni), ini keren tetapi tidak menyelesaikan satu masalah (secara strategis saya hanya bekerja pada versi LTS). Bagaimana cara mendapatkan file?

Menggunakan komposisi. Diberikan kode, menyebabkan saya sensasi bahwa ini ada di dalam modul, jadi, harus memiliki fungsi untuk melakukannya. Jika tidak, Anda harus menggunakan IIFE untuk menggabungkan kode peran ke dalam fungsi asinkron yang membuat modul sederhana yang melakukan semuanya untuk Anda, atau Anda dapat menggunakan komposisi yang benar.

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

Perhatikan bahwa nama variabel berubah karena semantik. Anda mengirimkan functor (fungsi yang dapat dipanggil oleh fungsi lain) dan menerima penunjuk pada memori yang berisi blok awal logika aplikasi.

Tapi, apakah itu bukan modul dan Anda perlu mengekspor logikanya?

Gabungkan fungsi dalam fungsi asinkron.

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

Atau ubah nama variabel, apapun ...


* oleh efek samping menans setiap efek bakteri dari aplikasi yang dapat mengubah status / perilaku atau memperkenalkan bug dalam aplikasi, seperti IO.

** dengan "murni", itu dalam apostrof karena fungsinya tidak murni dan kode dapat disatukan ke versi murni, ketika tidak ada output konsol, hanya manipulasi data.

Selain itu, untuk menjadi murni, Anda harus bekerja dengan monad yang menangani efek samping, yang rawan kesalahan, dan memperlakukan kesalahan itu secara terpisah dari aplikasi.

3 Babakness Feb 28 2018 at 11:41

Menggunakan Task, futurize, dan List yang dapat dijelajahi, Anda dapat melakukannya

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

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

Berikut adalah cara Anda menyiapkannya

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)

Cara lain untuk menyusun kode yang diinginkan adalah

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

Atau mungkin lebih berorientasi fungsional

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

Kemudian dari fungsi induk

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

Jika Anda benar-benar menginginkan lebih banyak fleksibilitas dalam pengkodean, Anda bisa melakukan ini (untuk bersenang-senang, saya menggunakan operator Pipe Forward yang diusulkan )

import { curry, flip } from 'ramda'

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

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

PS - Saya tidak mencoba kode ini di konsol, mungkin ada beberapa kesalahan ketik ... "gaya bebas lurus, dari atas kubah!" seperti yang dikatakan anak-anak 90-an. :-p

3 Beau Mar 13 2019 at 06:31

Saat ini properti prototipe Array.forEach tidak mendukung operasi asinkron, tetapi kita dapat membuat poly-fill kita sendiri untuk memenuhi kebutuhan kita.

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

Dan itu dia! Anda sekarang memiliki metode forEach asinkron yang tersedia pada larik apa pun yang ditentukan setelah ini ke operasi.

Mari kita uji ...

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

Kita bisa melakukan hal yang sama untuk beberapa fungsi array lainnya seperti map ...

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

... dan seterusnya :)

Beberapa hal yang perlu diperhatikan:

  • IteratorFunction Anda harus berupa fungsi atau janji async
  • Larik apa pun yang dibuat sebelumnya Array.prototype.<yourAsyncFunc> = <yourAsyncFunc>tidak akan memiliki fitur ini
3 PranavKAndro Nov 25 2019 at 03:31

Hari ini saya menemukan banyak solusi untuk ini. Menjalankan fungsi async await di forEach Loop. Dengan membangun pembungkus di sekitar kita dapat mewujudkannya.

Penjelasan lebih rinci tentang cara kerjanya secara internal, untuk forEach asli dan mengapa tidak dapat membuat panggilan fungsi asinkron dan detail lainnya tentang berbagai metode disediakan di tautan di sini

Berbagai cara yang melaluinya dapat dilakukan dan itu adalah sebagai berikut,

Metode 1: Menggunakan pembungkus.

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

Metode 2: Menggunakan yang sama sebagai fungsi umum 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');
            }
        })
      });
    };
  }

Penggunaan:

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

Metode 3:

Menggunakan Promise.all

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

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

Metode 4: Tradisional for loop atau modern for loop

// 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);
3 richytong May 21 2020 at 03:57

Anda dapat menggunakan Array.prototype.forEach, tetapi async / await tidak begitu kompatibel. Hal ini karena promise yang ditampilkan dari callback asinkron diharapkan akan diselesaikan, tetapi Array.prototype.forEachtidak menyelesaikan promise apa pun dari eksekusi callbacknya. Jadi, Anda dapat menggunakan forEach, tetapi Anda harus menangani sendiri resolusi promise tersebut.

Berikut ini cara membaca dan mencetak setiap file secara seri menggunakan 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
}

Berikut adalah cara (tetap menggunakan Array.prototype.forEach) untuk mencetak isi file secara paralel

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)
}
2 jgmjgm Oct 15 2019 at 01:35

Untuk melihat bagaimana itu bisa salah, cetak console.log di akhir metode.

Hal-hal yang bisa salah secara umum:

  • Pesanan sewenang-wenang.
  • printFiles dapat selesai dijalankan sebelum mencetak file.
  • Pertunjukkan yang buruk.

Ini tidak selalu salah tetapi sering kali dalam kasus penggunaan standar.

Umumnya, menggunakan forEach akan menghasilkan semua kecuali yang terakhir. Ini akan memanggil setiap fungsi tanpa menunggu fungsi yang berarti ia memberitahu semua fungsi untuk memulai kemudian selesai tanpa menunggu fungsi selesai.

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

Ini adalah contoh dalam JS asli yang akan mempertahankan urutan, mencegah fungsi kembali sebelum waktunya, dan secara teori mempertahankan kinerja yang optimal.

Ini akan:

  • Memulai semua pembacaan file terjadi secara paralel.
  • Pertahankan pesanan melalui penggunaan peta untuk memetakan nama file yang akan ditunggu.
  • Tunggu setiap janji dalam urutan yang ditentukan oleh array.

Dengan solusi ini, file pertama akan ditampilkan segera setelah tersedia tanpa harus menunggu yang lain tersedia terlebih dahulu.

Ini juga akan memuat semua file pada saat yang sama daripada harus menunggu yang pertama selesai sebelum pembacaan file kedua dapat dimulai.

Satu-satunya kekurangan ini dan versi aslinya adalah jika beberapa pembacaan dimulai sekaligus maka lebih sulit untuk menangani kesalahan karena memiliki lebih banyak kesalahan yang dapat terjadi pada satu waktu.

Dengan versi yang membaca file pada suatu waktu maka akan berhenti pada kegagalan tanpa membuang waktu mencoba membaca file lagi. Bahkan dengan sistem pembatalan yang rumit, mungkin sulit untuk menghindari kegagalan pada file pertama tetapi membaca sebagian besar file lainnya juga.

Performa tidak selalu bisa diprediksi. Sementara banyak sistem akan lebih cepat dengan file paralel membaca beberapa akan lebih memilih berurutan. Beberapa bersifat dinamis dan mungkin bergeser saat dimuat, pengoptimalan yang menawarkan latensi tidak selalu menghasilkan throughput yang baik dalam perselisihan yang berat.

Juga tidak ada penanganan kesalahan dalam contoh itu. Jika sesuatu mengharuskan mereka untuk berhasil ditampilkan atau tidak ditampilkan sama sekali, itu tidak akan berhasil.

Eksperimen mendalam direkomendasikan dengan console.log di setiap tahap dan solusi pembacaan file palsu (sebagai gantinya penundaan acak). Meskipun banyak solusi tampaknya melakukan hal yang sama dalam kasus sederhana, semuanya memiliki perbedaan halus yang membutuhkan pengawasan ekstra untuk diperas.

Gunakan tiruan ini untuk membantu membedakan antara solusi:

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

ScottRudiger Jun 21 2018 at 23:55

Mirip dengan Antonio Val p-iteration, modul npm alternatif adalah 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();

Atau, async-afmemiliki metode statis (log / logAF) yang mencatat hasil janji:

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

Namun, keuntungan utama dari library ini adalah Anda dapat menghubungkan metode asinkron untuk melakukan sesuatu seperti:

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