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?

user779159
quelle
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);
}());
Bergi
quelle
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);
Shusson
quelle
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();
        });
    });         
}
Ronnie Royston
quelle
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