Skip to content

Commit

Permalink
Add util/async.ts
Browse files Browse the repository at this point in the history
  • Loading branch information
ry committed May 17, 2019
1 parent c2f5a7c commit 6946665
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 0 deletions.
60 changes: 60 additions & 0 deletions util/async.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { assert } from "../testing/asserts.ts";

// TODO(ry) It'd be better to make Deferred a class that inherits from
// Promise, rather than an interface. This is possible in ES2016, however
// typescript produces broken code when targeting ES5 code.
// See https://github.com/Microsoft/TypeScript/issues/15202
// At the time of writing, the github issue is closed but the problem remains.
export interface Deferred<T> extends Promise<T> {
resolve: (value?: T | PromiseLike<T>) => void;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
reject: (reason?: any) => void;
}

/** Creates a Promise with the `reject` and `resolve` functions
* placed as methods on the promise object itself. It allows you to do:
*
* const p = deferred<number>();
* // ...
* p.resolve(42);
*/
export function deferred<T>(): Deferred<T> {
let methods;
const promise = new Promise<T>(
(resolve, reject): void => {
methods = { resolve, reject };
}
);
return Object.assign(promise, methods) as Deferred<T>;
}

export class Latch<T> {
// TODO(ry) Can this be done without using Arrays?

// Array of `[resolve_function, value]` tuples.
private sendQueue: Array<[() => void, T]> = [];
// Array of `resolve_function` values.
private recvQueue: Array<(value: T) => void> = [];

send(value: T): Promise<void> {
const recvResolve = this.recvQueue.shift();
if (recvResolve) {
recvResolve(value);
return Promise.resolve();
} else {
return new Promise((resolve, _) => this.sendQueue.push([resolve, value]));
}
}

recv(): Promise<T> {
const s = this.sendQueue.shift();
if (s) {
const [sendResolve, value] = s;
sendResolve();
return Promise.resolve(value);
} else {
return new Promise((res, _) => this.recvQueue.push(res));
}
}
}
27 changes: 27 additions & 0 deletions util/async_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { test, runIfMain } from "../testing/mod.ts";
import { assertEquals } from "../testing/asserts.ts";
import { Latch, deferred } from "./async.ts";

test(async function asyncDeferred(): Promise<void> {
const d = deferred<number>();
d.resolve(12);
});

async function send3(latch: Latch<number>): Promise<void> {
await latch.send(1);
await latch.send(2);
await latch.send(3);
}

test(async function asyncLatch(): Promise<void> {
const latch = new Latch<number>();
send3(latch);

assertEquals(1, await latch.recv());
assertEquals(2, await latch.recv());
assertEquals(3, await latch.recv());
let _lastPromise = latch.recv();
});

runIfMain(import.meta);
1 change: 1 addition & 0 deletions util/test.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
import "./async_test.ts";
import "./deep_assign_test.ts";

0 comments on commit 6946665

Please sign in to comment.