diff --git a/@sammo/util/src/index.ts b/@sammo/util/src/index.ts index 4595043..2c2c730 100644 --- a/@sammo/util/src/index.ts +++ b/@sammo/util/src/index.ts @@ -5,6 +5,7 @@ export * from "./unwrap.js" export * from "./must.js" export * from "./types.js" export * from "./web.js" +export * from "./signal.js" export * from "./strongType.js" diff --git a/@sammo/util/src/signal.ts b/@sammo/util/src/signal.ts new file mode 100644 index 0000000..15bbc0c --- /dev/null +++ b/@sammo/util/src/signal.ts @@ -0,0 +1,102 @@ + + +type RejectType = { + type: 'reject'; + reason: string | Error; +}; + +type ResolveType = { + type: 'resolve'; + value?: T; +}; + +type Response = ResolveType | RejectType | { + type: 'timeout' | 'abort'; +}; + +const enum TimeoutState { + notYetWait = 0, + waiting = 1, + done = 2, + timeout = 3, +}'' + +/** + * 재사용 가능한 Signal 클래스 + */ +export class Signal { + private resolved: boolean = false; + private timeoutState: TimeoutState = TimeoutState.notYetWait; + + private _promise!: Promise>; + private _resolve!: (value: Response) => void; + private timeoutID?: ReturnType; + + constructor(public readonly timeoutMs?: number) { + this.reset(); + } + + public reset(): void { + if(this.timeoutID){ + clearTimeout(this.timeoutID); + this.timeoutID = undefined; + } + + this.resolved = false; + this.timeoutState = TimeoutState.notYetWait; + this._promise = new Promise((resolve) => { + this._resolve = resolve; + }); + + if(this.timeoutMs){ + this.timeoutID = setTimeout(() => { + this._timeout(); + }, this.timeoutMs); + } + } + + private _timeout(){ + if(this.timeoutState === TimeoutState.waiting){ + this.resolved = true; + this._resolve({type: 'timeout'}); + } + this.timeoutState = TimeoutState.timeout; + } + + public async wait(): Promise>{ + if(this.timeoutState === TimeoutState.timeout){ + this.reset(); + return {type: 'timeout'}; + } + this.timeoutState = TimeoutState.waiting; + const result = await this._promise; + this.reset(); + return result; + } + + public async abort(): Promise { + if(this.resolved){ + return; + } + this.resolved = true; + this.timeoutState = TimeoutState.done; + if(this.timeoutID){ + clearTimeout(this.timeoutID); + this.timeoutID = undefined; + } + this._resolve({type: 'abort'}); + } + + public async notify(value?: T): Promise { + if(this.resolved){ + return; + } + this.resolved = true; + this.timeoutState = TimeoutState.done; + if(this.timeoutID){ + clearTimeout(this.timeoutID); + this.timeoutID = undefined; + } + this._resolve({type: 'resolve', value}); + } +} \ No newline at end of file