Replies: 9 comments 2 replies
-
They can be polyfilled. |
Beta Was this translation helpful? Give feedback.
-
General guides:
|
Beta Was this translation helpful? Give feedback.
-
Open questions;
|
Beta Was this translation helpful? Give feedback.
-
Upstream issues:
However, whether it can be used with |
Beta Was this translation helpful? Give feedback.
-
ADB Sync: wait vs errorEvery calls to Each Before stream rewrite, each class AdbSync {
socket: AdbSocket;
sendLock: AutoResetEvent;
async command1() {
await this.sendLock.wait();
try {
// send/receive packets
this.socket.write(new ArrayBuffer(0));
} finally {
this.sendLock.notify();
}
}
} After stream rewrite, it's possible to re-use the lock mechanism of the class AdbSync {
writable: WritableStream<ArrayBuffer>;
async command1() {
// Throw if stream is already locked
const writer = this.writable.getWriter();
try {
// send/receive packets
writer.write(new ArrayBuffer(0));
} finally {
writer.releaseLock();
}
}
} In fact, because each command is implemented in their own methods, it can be more encapsulated. function command1(writable: WritableStream<ArrayBuffer>) {
const writer = writable.getWriter();
try {
// send/receive packets
writer.write(new ArrayBuffer(0));
return {};
} finally {
writer.releaseLock();
}
}
class AdbSync {
writable: WritableStream<ArrayBuffer>;
async command1() {
return command1(this.writable);
}
} Continuing using the public write(
filename: string,
mode?: number,
mtime?: number,
): WritableStream<ArrayBuffer> {
return new HookWritableStream({
start: async () => {
await this.sendLock.wait();
return {
writable: adbSyncPush(
this.stream,
this.writer,
filename,
mode,
mtime,
),
state: {},
};
},
close: async () => {
this.sendLock.notify();
}
});
} On the other hand, it's not possible to get notified when a |
Beta Was this translation helpful? Give feedback.
-
Stream chunk type:
|
Beta Was this translation helpful? Give feedback.
-
Replace
|
Beta Was this translation helpful? Give feedback.
-
Vercel doesn't support Node.js 16Vercel currently only support Node.js LTS 12 and 14, but It's possible to remove all usage of streams from Server side (they only make sense on client side, when a device is connected), but adds unnecessary complexity. |
Beta Was this translation helpful? Give feedback.
-
Specs using Streams API
|
Beta Was this translation helpful? Give feedback.
-
1. Current design
Now
AdbSocket
is a ordinary class.It directly contains read events and write/close operations.
1.1
onData
onData
is a special event emitter, it pauses the socket until first event handler is registered.This is because I forgot that JavaScript is single-threaded, and worried about the dispatcher can read data from device and fire the event before user code can attach a listener.
1.2
onClose
onClose
is also a special event emitter. After fired, newly attached event listeners will be invoked immediately (It latches the state).1.3
write()
Simply write data to the other end.
2. Web Streams
Web Streams API defines standardized stream API for Web and Node.js.
It mainly contains three classes:
ReadableStream
WriteableStream
TransformStream
3. Proposed design
3.1 Basic
Because
ReadableStream
andWriteableStream
are both classes, they can't be extended by a single class. So as the spec recommends, a duplex stream is implemented by an object withreadable
andwritable
properties:3.2
readable
readable
ReadableStream will replaceonData
event andonClose
event.3.2.1
cancel
Calling
cancel
method onreadable
or one of its readers will close bothreadable
andwritable
(and the socket itself).3.2.2 Use patterns
onData
event is a "push" model, whilereadable
is a "pull" model. Converting toreadable
means changes to consumers.Now
onData
is used in:AdbLegacyShell
: It pipesonData
events to itsonStdout
event:ya-webadb/libraries/adb/src/commands/shell/legacy.ts
Line 34 in e6b751c
ya-webadb/libraries/adb/src/commands/shell/legacy.ts
Lines 38 to 41 in e6b751c
After conversion: it will need a "pull loop".
createSocketAndReadAll
util method: Collects all data from asocket
:ya-webadb/libraries/adb/src/adb.ts
Lines 233 to 237 in 41a9565
After conversion: It can use
readable
as an async iterator. This will be simpler.AdbBufferedStream
: Most consumers usesAdbBufferedStream
to read exactly N bytes from the socket, ignoring packet boundaries.After conversion:
AdbBufferedStream
is still required, becauseReadableStream
doesn't support reading exactly N bytes.3.3
writable
writable
WritableStream will replacewrite
method.3.3.1
close
Calling
close
method onwritable
or one of its writers will close bothreadable
andwriteable
(and the socket itself).3.4
StructTransformStream
Web Streams support both byte stream and object stream.
Struct
deserialization can be wrapped in aTransformStream
.pseudo code:
It also allows more separation between stream parsing and processing.
4. Advantages
5. Disadvantages
More complicate.
Now the consumer must maintain 5 objects (vs 1):
AdbSocket
readable
writable
Incorrectly calling
getReader
/getWriter
will lock the program.It has 4 (or maybe 5)
close
methods (readable
, reader,writable
, writer, maybe alsoAdbSocket
).6. Unresolved Questions
6.1 Node.js doesn't expose Web Streams classes on global
In web browsers,
ReadableStream
andWriteableStream
are exposed onWindow
.In Node.js, they exist in
stream/web
module.Possible solutions:
Create a helper package:
stream/web
for Node.jsbrowser
field in package.json to let bundlers choose a different implementation.Downside: Requires a bundler, can't be used as native ESM in browsers.
Abuse the obsoleted commonjs module spec, with allows synchronized optional dependency
let m; try { m = require("m") } catch {}
Downside: Still doesn't work in native ESM, but requires less special treatment by bundlers.
Require users to re-assign them to global in Node.js
Downside: Global pollution.
6.2 What should
abort
method onwritable
do?Beta Was this translation helpful? Give feedback.
All reactions