import { DestroyContainer } from "base/DestroyContainer"; import { Rejections } from "base/Rejections"; import { Silence } from "base/Silence"; import { ensureFunction } from "helpers/ensures"; import { ConstructorType } from "types/ConstructorType"; import { DestroyableType } from "types/DestroyableType"; import { MessageType } from "types/MessageType"; export type MessageExecutorType = ( resolve: ConstructorType<[T]>, reject: ConstructorType<[unknown]>, name?: string, ) => MessageType | (() => void) | void; /** * A message created from an executor function. * The executor function can return a message destruction function. * * @url https://silentium.pw/article/message/view */ export function Message(executor: MessageExecutorType) { return new MessageImpl(executor); } /** * Reactive message implementation * * @url https://silentium.pw/article/message/view */ export class MessageImpl implements MessageType, DestroyableType { private myName = "unknown"; public constructor( private executor: MessageExecutorType, private rejections = Rejections(), private dc = DestroyContainer(), ) { ensureFunction(executor, "Message: executor"); } public then( resolve: ConstructorType<[T]>, rejected?: ConstructorType<[unknown]>, ) { if (this.dc.destroyed()) { return this; } const newMessageRejections = Rejections(); if (rejected) { newMessageRejections.catch(rejected); } const newMessageDc = DestroyContainer(); const newMessage = new MessageImpl( this.executor, newMessageRejections, newMessageDc, ); newMessage.catch(this.rejections.reject); this.dc.add(newMessage); try { const mbDestructor = this.executor( Silence(function messageResolver(value: T) { if (!newMessageDc.destroyed()) { resolve(value); } }), newMessageRejections.reject, this.myName, ); newMessageDc.add(mbDestructor); } catch (e: any) { newMessageRejections.reject(e); } return newMessage; } public catch(rejected: ConstructorType<[unknown]>) { if (this.dc.destroyed()) { return this; } this.rejections.catch(rejected); return this; } public destroy() { try { this.dc.destroy(); this.rejections.destroy(); } catch (e) { this.rejections.reject(e); } return this; } public destroyed() { return this.dc.destroyed(); } public name(newName: string) { this.myName = newName; return this; } }