Strim dalam Node.js sangat berkuasa Ia menyediakan sokongan untuk memproses fail yang berpotensi besar, dan juga pemprosesan dan penghantaran data abstrak dalam beberapa senario. Kerana ia sangat mudah untuk digunakan, dalam pertempuran sebenar kami sering menulis beberapa fungsi alat/perpustakaan berdasarkannya Namun, selalunya disebabkan oleh kecuaian kami terhadap ciri-ciri tertentu aliran, fungsi/pustaka yang kami tulis tidak akan memenuhi keperluan dalam beberapa kes. . kesan yang diingini, atau meletakkan beberapa lombong tersembunyi. Artikel ini akan memberikan dua petua yang saya fikir berguna semasa menulis alatan berasaskan aliran.
1. Berwaspada dengan kebocoran memori EVENTEMITTER
Dalam fungsi yang mungkin dipanggil beberapa kali, jika anda perlu menambah pendengar acara pada strim untuk melaksanakan operasi tertentu. Kemudian anda perlu berhati-hati dengan kebocoran memori yang disebabkan oleh penambahan pendengar:
'use strict'; const fs = require('fs'); const co = require('co'); function getSomeDataFromStream (stream) { let data = stream.read(); if (data) return Promise.resolve(data); if (!stream.readable) return Promise.resolve(null); return new Promise((resolve, reject) => { stream.once('readable', () => resolve(stream.read())); stream.on('error', reject); stream.on('end', resolve); }) } let stream = fs.createReadStream('/Path/to/a/big/file'); co(function *() { let chunk; while ((chunk = yield getSomeDataFromStream(stream)) !== null) { console.log(chunk); } }).catch(console.error);
Dalam kod di atas, fungsi getSomeDataFromStream akan melengkapkan Promise apabila strim melaporkan ralat atau tiada data dengan mendengar peristiwa ralat dan acara tamat. Walau bagaimanapun, apabila melaksanakan kod, kami akan melihat mesej penggera dalam konsol: (nod) amaran: kemungkinan kebocoran memori EventEmitter dikesan 11 pendengar ralat ditambah Gunakan emitter.setMaxListeners() untuk meningkatkan had., kerana kami berada dalam setiap Setiap kali fungsi ini dipanggil, pendengar acara ralat tambahan dan pendengar acara tamat ditambahkan pada strim masuk. Untuk mengelakkan potensi kebocoran memori ini, kami perlu memastikan bahawa selepas setiap pelaksanaan fungsi, semua pendengar tambahan yang ditambahkan oleh panggilan ini dikosongkan untuk memastikan fungsi bebas pencemaran:
function getSomeDataFromStream (stream) { let data = stream.read(); if (data) return Promise.resolve(data); if (!stream.readable) return Promise.resolve(null); return new Promise((resolve, reject) => { stream.once('readable', onData); stream.on('error', onError); stream.on('end', done); function onData () { done(); resolve(stream.read()); } function onError (err) { done(); reject(err); } function done () { stream.removeListener('readable', onData); stream.removeListener('error', onError); stream.removeListener('end', done); } }) }
Kedua, pastikan panggilan balik fungsi alat dipanggil selepas memproses data
Fungsi alat selalunya menyediakan parameter fungsi panggil balik kepada dunia luar Selepas semua data dalam strim diproses, ia dicetuskan dengan nilai yang ditentukan Pendekatan biasa adalah untuk menggantung fungsi panggil balik dalam acara akhir strim , tetapi jika ia diproses Fungsi adalah operasi tak segerak yang memakan masa dan fungsi panggil balik boleh dipanggil sebelum semua data diproses:
'use strict'; const fs = require('fs'); let stream = fs.createReadStream('/Path/to/a/big/file'); function processSomeData (stream, callback) { stream.on('data', (data) => { // 对数据进行一些异步耗时操作 setTimeout(() => console.log(data), 2000); }); stream.on('end', () => { // ... callback() }) } processSomeData(stream, () => console.log('end'));
Panggil balik kod di atas boleh dipanggil apabila semua data belum diproses, kerana peristiwa akhir strim hanya dicetuskan apabila data dalam strim dibaca. Jadi kita perlu menyemak tambahan sama ada data telah diproses:
function processSomeData (stream, callback) { let count = 0; let finished = 0; let isEnd = false; stream.on('data', (data) => { count++; // 对数据进行一些异步耗时操作 setTimeout(() => { console.log(data); finished++; check(); }, 2000); }); stream.on('end', () => { isEnd = true; // ... check(); }) function check () { if (count === finished && isEnd) callback() } }
Dengan cara ini, panggilan balik akan dicetuskan selepas semua data diproses.