Skip to content

Commit

Permalink
lib: make diagnostics_channel async iterable
Browse files Browse the repository at this point in the history
  • Loading branch information
Qard committed Oct 31, 2020
1 parent 7ebae13 commit 6548fcb
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 1 deletion.
74 changes: 73 additions & 1 deletion lib/diagnostics_channel.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@
const {
ArrayPrototypeIndexOf,
ArrayPrototypePush,
ArrayPrototypeShift,
ArrayPrototypeSplice,
ObjectCreate,
ObjectGetPrototypeOf,
ObjectSetPrototypeOf,
Promise,
PromiseResolve,
SymbolAsyncIterator,
SymbolHasInstance,
WeakRefPrototypeGet
} = primordials;
Expand All @@ -21,8 +25,72 @@ const { triggerUncaughtException } = internalBinding('errors');

const { WeakReference } = internalBinding('util');

class AsyncIterableChannel {
constructor(channel) {
this.channel = channel;
this.events = [];
this.waiting = [];

this.subscriber = (message) => {
const resolve = ArrayPrototypeShift(this.waiting);
if (resolve) {
return resolve({
value: message,
done: false
});
}

ArrayPrototypePush(this.events, message);
};

channel.subscribe(this.subscriber);
}

[SymbolAsyncIterator]() {
return this;
}

return() {
const data = { done: true };
this.done = true;

this.channel.unsubscribe(this.subscriber);

for (let i = 0; i < this.waiting.length; i++) {
const resolve = this.waiting[i];
resolve(data);
}

return PromiseResolve(data);
}

next() {
const event = ArrayPrototypeShift(this.events);
if (event) {
return PromiseResolve({
value: event,
done: false
});
}

if (this.done) {
return PromiseResolve({
done: true
});
}

return new Promise((resolve) => {
ArrayPrototypePush(this.waiting, resolve);
});
}
}

// TODO(qard): should there be a C++ channel interface?
class ActiveChannel {
[SymbolAsyncIterator]() {
return new AsyncIterableChannel(this);
}

subscribe(subscription) {
if (typeof subscription !== 'function') {
throw new ERR_INVALID_ARG_TYPE('subscription', ['function'],
Expand Down Expand Up @@ -71,7 +139,11 @@ class Channel {
static [SymbolHasInstance](instance) {
const prototype = ObjectGetPrototypeOf(instance);
return prototype === Channel.prototype ||
prototype === ActiveChannel.prototype;
prototype === ActiveChannel.prototype;
}

[SymbolAsyncIterator]() {
return new AsyncIterableChannel(this);
}

subscribe(subscription) {
Expand Down
31 changes: 31 additions & 0 deletions test/parallel/test-diagnostics-channel-async-iterable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
'use strict';

const common = require('../common');
const dc = require('diagnostics_channel');
const assert = require('assert');

const input = {
foo: 'bar'
};

const channel = dc.channel('test');

const done = common.mustCall();

async function main() {
for await (const message of channel) {
assert.strictEqual(channel.hasSubscribers, true);
assert.strictEqual(message, input);
break;
}

// Make sure the subscription is cleaned up when breaking the loop!
assert.strictEqual(channel.hasSubscribers, false);
done();
}

main();

setTimeout(common.mustCall(() => {
channel.publish(input);
}), 1);

0 comments on commit 6548fcb

Please sign in to comment.