Wie verwende ich ES8 async / await mit Streams?


74

In https://stackoverflow.com/a/18658613/779159 finden Sie ein Beispiel für die Berechnung des MD5 einer Datei mithilfe der integrierten Kryptobibliothek und der integrierten Streams.

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

Aber ist es möglich, dies in die Verwendung von ES8 async / await umzuwandeln, anstatt den oben gezeigten Rückruf zu verwenden, aber dennoch die Effizienz der Verwendung von Streams beizubehalten?


2
async/awaitist nichts anderes als Unterstützung auf Syntaxebene für Versprechen. Wenn Sie diesen Code in ein Versprechen einfügen können, sind Sie fertig.
Felix Kling

Node.js 10.x unterstützt for-await-ofdas Lesen von Streams ( nodejs.org/docs/latest-v10.x/api/… ), aber ich denke, es ist nicht das richtige Konzept für Ihre Frage hier. Lassen Sie es als Notiz für andere, die hierher kommen könnten, in einer Situation, in der es helfen würde.
SimonSimCity

Antworten:


102

asyncIch awaitarbeite nur mit Versprechungen, nicht mit Streams. Es gibt Ideen, einen zusätzlichen Stream-ähnlichen Datentyp zu erstellen, der eine eigene Syntax erhält, aber diese sind, wenn überhaupt, sehr experimentell und ich werde nicht auf Details eingehen.

Auf jeden Fall wartet Ihr Rückruf nur auf das Ende des Streams, was perfekt zu einem Versprechen passt. Sie müssten nur den Stream einpacken:

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

Jetzt können Sie dieses Versprechen abwarten:

(async function() {
    let sha1sum = await end;
    console.log(sha1sum);
}());

3
Herzliche Glückwünsche! Jetzt ist es keine richtige Antwort, jetzt können Sie den asynchronen Zyklus mit Streams verwenden . 2ality.com/2018/04/async-iter-nodejs.html . Leider ist es jetzt ein experimentelles Feature.
MiF

9
@MiF Du meinst also, die Antwort ist falsch, weil sie nicht "hoch experimentell" ist, sondern nur "experimentell"? :-D
Bergi

JS verwendet in allen asynchronen Fällen Versprechen und Async / Wait. Es ist zu seltsam, dass diese Fiktion nicht in der Produktion veröffentlicht wurde. Ich denke, es ist jetzt ein guter Sintacsys und wird sich nicht ändern.
MiF

Warum warten Sie auf den ReadStream end? Würde das Versprechen nicht aufgelöst, bevor die Hash-Funktion die Daten ausführen kann? Oder würde es zumindest nicht eine Rennbedingung schaffen? Ich würde annehmen, dass Sie den Wert von fd.pipe(hash)in einer Variablen speichern und auf das finishEreignis warten möchten, da dies signalisieren würde, dass der WriteableStream (Hashing) abgeschlossen ist.
Adam-Beck

@ Adam-Beck Klingt so, als hättest du recht. Warum habe ich das so gemacht? Weil OP das auch in ihrer Frage getan hat. Fühlen Sie sich frei, eine Bearbeitung vorzuschlagen.
Bergi

48

Wenn Sie die Knotenversion> = v10.0.0 verwenden, können Sie stream.pipeline und util.promisify verwenden .

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

Sollte Unpipe auch irgendwo genannt werden?
Fluous

1
Ich musste stundenlang graben, bis ich diese Antwort gefunden habe. Warum ist dies nicht als Beispiel in den Node Streams-Dokumenten enthalten ...?
Emzero

Aber es ist in den Dokumenten. nodejs.org/api/…
user7339839

Dies ist eine bessere Antwort
Saille

14

So etwas funktioniert:

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

Tolle Erklärung, ich habe dieses Versprechen verwendet, um eine https-Anfrage zu verpacken, die ich mit dem in https integrierten NodeJS-Modul mache. Vielen Dank!
Alex Rindone
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.