mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-26 06:34:20 +08:00
122 lines
3.9 KiB
JavaScript
122 lines
3.9 KiB
JavaScript
// Readable streams built by constructor functions, the from2 pattern:
|
|
// util.inherits(Ctor, Readable), Readable.call(this, opts) and _read on
|
|
// the prototype.
|
|
const { Readable } = require("stream");
|
|
const util = require("util");
|
|
|
|
function Source(list, opts) {
|
|
if (!(this instanceof Source)) return new Source(list, opts);
|
|
this._list = list.slice();
|
|
// Before Readable.call there is no state: Node's setter ignores this.
|
|
this.destroyed = false;
|
|
Readable.call(this, opts || {});
|
|
this._reads = 0;
|
|
console.log("hwm", this._readableState.highWaterMark, "objectMode", this._readableState.objectMode);
|
|
}
|
|
util.inherits(Source, Readable);
|
|
Source.prototype._read = function (size) {
|
|
this._reads++;
|
|
const item = this._list.shift();
|
|
this.push(item === undefined ? null : Buffer.from(item));
|
|
};
|
|
|
|
const s = Source(["alpha", "beta", "gamma"]);
|
|
console.log("instanceof", s instanceof Source, s instanceof Readable, Source.super_ === Readable);
|
|
console.log("readable", s.readable, "ended", s.readableEnded, "destroyed", s.destroyed);
|
|
console.log("flowing", s.readableFlowing, s._readableState.flowing);
|
|
s.on("data", function (chunk) {
|
|
console.log("data", this === s, Buffer.isBuffer(chunk), chunk.toString());
|
|
});
|
|
s.on("end", function () {
|
|
console.log("end", this === s, s.readableEnded, s._readableState.ended, s._reads);
|
|
});
|
|
s.on("close", () => {
|
|
console.log("close", s.destroyed, s.closed);
|
|
paused();
|
|
});
|
|
console.log("sync after on('data')", s.readableFlowing);
|
|
|
|
// from2's callback-driven _read, asynchronous pushes and a small
|
|
// highWaterMark.
|
|
function From(read, opts) {
|
|
if (!(this instanceof From)) return new From(read, opts);
|
|
this._reading = false;
|
|
this._callback = check;
|
|
this.destroyed = false;
|
|
Readable.call(this, opts);
|
|
const self = this;
|
|
const hwm = this._readableState.highWaterMark;
|
|
function check(err, data) {
|
|
if (self.destroyed) return;
|
|
if (err) return self.destroy(err);
|
|
if (data === null) return self.push(null);
|
|
self._reading = false;
|
|
if (self.push(data)) self._read(hwm);
|
|
}
|
|
this._from = read;
|
|
}
|
|
util.inherits(From, Readable);
|
|
From.prototype._read = function (size) {
|
|
if (this._reading || this.destroyed) return;
|
|
this._reading = true;
|
|
this._from(size, this._callback);
|
|
};
|
|
From.prototype.destroy = function (err) {
|
|
if (this.destroyed) return;
|
|
this.destroyed = true;
|
|
const self = this;
|
|
process.nextTick(function () {
|
|
if (err) self.emit("error", err);
|
|
self.emit("close");
|
|
});
|
|
};
|
|
|
|
function paused() {
|
|
const parts = ["one ", "two ", "three"];
|
|
const r = new From(function (size, next) {
|
|
setImmediate(() => next(null, parts.length ? parts.shift() : null));
|
|
}, { highWaterMark: 4 });
|
|
console.log("from hwm", r._readableState.highWaterMark, r.readableHighWaterMark);
|
|
r.setEncoding("utf8");
|
|
let text = "";
|
|
r.on("readable", () => {
|
|
let chunk;
|
|
while ((chunk = r.read()) !== null) text += chunk;
|
|
});
|
|
r.on("end", () => {
|
|
console.log("paused text", JSON.stringify(text), typeof text);
|
|
// From() without new runs outside the listener, where `this` is unset.
|
|
setImmediate(failing);
|
|
});
|
|
}
|
|
|
|
function failing() {
|
|
let calls = 0;
|
|
const r = From(function (size, next) {
|
|
calls++;
|
|
if (calls === 2) return next(new Error("source failed"));
|
|
next(null, "chunk" + calls);
|
|
});
|
|
r.on("data", (d) => console.log("failing data", String(d)));
|
|
r.on("error", (e) => console.log("failing error", e.message, r.destroyed));
|
|
r.on("close", () => {
|
|
console.log("failing close", calls);
|
|
unshifted();
|
|
});
|
|
}
|
|
|
|
function unshifted() {
|
|
const r = new Source(["xyz"]);
|
|
r.once("readable", () => {
|
|
const first = r.read(1);
|
|
console.log("read(1)", String(first), r.readableLength);
|
|
r.unshift(first);
|
|
console.log("after unshift", r.readableLength);
|
|
r.pause();
|
|
console.log("isPaused", r.isPaused());
|
|
r.on("data", (d) => console.log("rest", String(d)));
|
|
r.resume();
|
|
});
|
|
r.on("end", () => console.log("unshifted end"));
|
|
}
|