Skip to content

Instantly share code, notes, and snippets.

@CMCDragonkai
Last active April 5, 2021 06:27
Show Gist options
  • Save CMCDragonkai/31d99cfa8a94bba381ca4537fc114e71 to your computer and use it in GitHub Desktop.
Save CMCDragonkai/31d99cfa8a94bba381ca4537fc114e71 to your computer and use it in GitHub Desktop.
Duplex Async Generator #javascript #typescript

Duplex Async Generator

This code demonstrates the how to combine a reading generator and a writing generator together into a duplex generator.

The duplex generator will simultaneously read and write at the same time from both the readable and writable generator.

Throwing an exception throws it to the writable generator, but we continue on (but make sure to skip 1 iteration by using the errored flag).

We use the null value to indicate the ending of the generator, it ends both reading and writing sides.

Running gives:

START READ
START WRITE: A
FINISH READ
FINISH WRITE: A
{ value: { value: 'READ: 1', done: false }, done: false }
GOT AN ERROR
START READ
START WRITE: B
FINISH READ
FINISH WRITE: B
{ value: { value: 'READ: 2', done: false }, done: false }
START READ
START WRITE: C
FINISH READ
FINISH WRITE: C
{ value: { value: 'READ: 3', done: false }, done: false }
END READ
END WRITE
END DUPLEX
{ value: undefined, done: true }
async function sleep(ms: number) {
return await new Promise((r) => setTimeout(r, ms));
}
let i = 1;
async function * readable (): AsyncGenerator<string, any, null|void> {
let close;
while (true) {
console.log('START READ');
await sleep(100);
console.log('FINISH READ');
close = yield 'READ: ' + i;
if (close === null) {
console.log('END READ');
break;
}
i++;
}
}
async function * writable (): AsyncGenerator<unknown,any, string|null> {
let write;
let errored;
while (true) {
errored = false;
try {
write = yield;
} catch (e) {
errored = true;
console.log('GOT AN ERROR');
}
if (!errored) {
if (write === null) {
console.log('END WRITE');
break;
} else {
console.log('START WRITE: ' + write);
await sleep(100);
console.log('FINISH WRITE: ' + write);
}
}
}
}
async function * duplex (): AsyncGenerator<string|void, any, string|null> {
const gR = readable();
const gW = writable();
// start the generator to the first yield
gW.next();
let vR;
let vW;
let errored;
while (true) {
errored = false;
try {
if (vR == null) {
vW = yield;
} else {
vW = yield vR;
}
} catch (e) {
errored = true;
gW.throw(e);
}
if (!errored) {
const ps = [];
if (vW === null) {
await Promise.all([gR.next(null), gW.next(null)]);
console.log('END DUPLEX');
break;
} else {
[vR,] = await Promise.all([gR.next(), gW.next(vW)])
}
}
}
}
async function main () {
const gD = duplex();
// start the generator to the first yield
gD.next();
var result1 = await gD.next('A');
console.log(result1);
gD.throw(new Error);
var result2 = await gD.next('B');
console.log(result2);
var result3 = await gD.next('C');
console.log(result3);
var result4 = await gD.next(null);
console.log(result4);
}
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment