javascript - Hvordan bruke ES8 async/await med strømmer?
I https://stackoverflow.com/a/18658613/779159 er det et eksempel på hvordan man beregner md5 av en fil ved å bruke det innebygde crypto-biblioteket og strømmer.
var fs = require('fs');
var crypto = require('crypto');
// the file you want to get the hash
var fd = fs.createReadStream('/some/file/name.txt');
var hash = crypto.createHash('sha1');
hash.setEncoding('hex');
fd.on('end', function() {
hash.end();
console.log(hash.read()); // the desired sha1sum
});
// read all file and pipe it (write it) to the hash object
fd.pipe(hash);
Men er det mulig å konvertere dette til å bruke ES8 async/await i stedet for å bruke callbacken som vist ovenfor, men samtidig beholde effektiviteten ved å bruke streams?
Svar #1
Nøkkelordet await fungerer bare på løfter (promises), ikke på strømmer. Det finnes ideer om å lage en ekstra strøm-lignende datatyp som ville få sin egen syntaks, men de er svært eksperimentelle om de i det hele tatt finnes, og jeg vil ikke gå inn på detaljer.
Uansett, tilbakeringingen din venter bare på slutten av strømmen, noe som passer perfekt for et løfte. Du må bare pakke inn strømmen:
var fd = fs.createReadStream('/some/file/name.txt');
var hash = crypto.createHash('sha1');
hash.setEncoding('hex');
// read all file and pipe it (write it) to the hash object
fd.pipe(hash);
var end = new Promise(function(resolve, reject) {
hash.on('end', () => resolve(hash.read()));
fd.on('error', reject); // or something like that. might need to close `hash`
});
Det finnes også en hjelpefunksjon for å gjøre nettopp det i nyere versjoner av Node.js - pipeline fra stream/promises-modulen:
import { pipeline } from 'node:stream/promises';
const fd = fs.createReadStream('/some/file/name.txt');
const hash = crypto.createHash('sha1');
hash.setEncoding('hex');
// read all file and pipe it (write it) to the hash object
const end = pipeline(fd, hash);
Nå kan du vente på det løftet:
(async function() {
let sha1sum = await end;
console.log(sha1sum);
}());
Svar #2
Hvis du bruker node-versjon >= v10.0.0, kan du bruke stream.pipeline og util.promisify.
const fs = require('fs');
const crypto = require('crypto');
const util = require('util');
const stream = require('stream');
const pipeline = util.promisify(stream.pipeline);
const hash = crypto.createHash('sha1');
hash.setEncoding('hex');
async function run() {
await pipeline(
fs.createReadStream('/some/file/name.txt'),
hash
);
console.log('Pipeline succeeded');
}
run().catch(console.error);
Svar #3
Node V15 har nå en promisfiy-pipeline i stream/promises. Dette er den reneste og mest offisielle måten.
const { pipeline } = require('stream/promises');
async function run() {
await pipeline(
fs.createReadStream('archive.tar'),
zlib.createGzip(),
fs.createWriteStream('archive.tar.gz')
);
console.log('Pipeline succeeded.');
}
run().catch(console.error);
Vi bør alle sette pris på hvor mye arbeid som er gjort her:
- Fang feil i alle strømmene.
- Ødelegg uferdige strømmer når en feil oppstår.
- Returner bare når den siste skrivbare strømmen er ferdig.
Denne pipe-greien er en av de mest kraftfulle funksjonene Node.JS har. Å gjøre den fullt asynkron er ikke lett. Nå har vi det.
Svar #4
2021 Oppdatering:
Nytt eksempel fra Node-dokumentasjonen:
async function print(readable) {
readable.setEncoding('utf8');
let data = '';
for await (const chunk of readable) {
data += chunk;
}
console.log(data);
}
se https://nodejs.org/api/stream.html#stream_readable_symbol_asynciterator
Svar #5
Noe sånt fungerer:
for (var res of fetchResponses){ //node-fetch package responses
const dest = fs.createWriteStream(filePath,{flags:'a'});
totalBytes += Number(res.headers.get('content-length'));
await new Promise((resolve, reject) => {
res.body.pipe(dest);
res.body.on("error", (err) => {
reject(err);
});
dest.on("finish", function() {
resolve();
});
});
}
