constructor()

in remote/communicator.js [57:203]


    constructor(channelProxy, options) {

        if (!isChannelProxy(channelProxy)) {
            throw new Error("channelProxy must be a IChannelProxy object.");
        }

        super();

        /** 
         * @private
         * @type {Array.<IRoute>}
         */
        this.routes = [];

        /** 
         * @private
         * @type {Object.<string, IPromiseResolver>} 
         */
        this.ongoingPromiseDict = Object.create(null);

        /**
         * @public
         * @readonly 
         * @type {string}
         */
        this.id = random.generateUid(6);

        if (options) {
            if (utils.isString(options.id)
                && !utils.string.isEmptyOrWhitespace(options.id)) {
                this.id = options.id;
            }

            if (utils.isNumber(options.timeout)) {
                /**
                 * @readonly
                 * @type {number}
                 */
                this.timeout = options.timeout;
            }
        }

        /** 
         * @private
         * @type {Donuts.Remote.IChannelProxy}
         */
        this.channelProxy = channelProxy;

        /**
         * @private
         * @param {Donuts.Remote.IChannelProxy} channel
         * @param {Donuts.Remote.IMessage} msg
         * @returns {Promise<void>}
         */
        this.onMessageAsync = async (channel, msg) => {
            const promise = this.ongoingPromiseDict[msg.id];

            /** @type {number} */
            const receivedTimestamp = Date.now();

            Log.writeInfoAsync("{} REMOTE {,7} {} ~{,4:F0}ms <= {}:{}", this.id, Action.Receive, msg.id, receivedTimestamp - msg.timestamp, msg.sender, msg.path);

            if (promise) {
                delete this.ongoingPromiseDict[msg.id];
                msg.succeeded ? promise.resolve(msg.body) : promise.reject(msg.body);

            } else if (utils.isNullOrUndefined(msg.succeeded)) {
                /** @type {Donuts.Remote.IRoutePathInfo} */
                let pathInfo;

                /** @type {Donuts.Remote.AsyncRequestHandler} */
                let asyncHandler;

                // Find the corresponding route and
                // generate the pathInfo.
                for (const route of this.routes) {
                    asyncHandler = route.asyncHandler;
                    pathInfo = route.pattern.match(msg.path);

                    if (pathInfo) {
                        break;
                    }
                }

                if (!pathInfo) {
                    return;
                }

                /** @type {*} */
                let response;

                /** @type {boolean} */
                let succeeded;

                try {
                    response = await asyncHandler(this, pathInfo, msg.body);
                    succeeded = true;

                } catch (exception) {
                    response = exception;
                    succeeded = false;

                    // @ts-ignore
                    if (response instanceof Error && !response["toJSON"]) {
                        // @ts-ignore
                        response.toJSON = ErrorToJSON;
                    }
                }

                /** @type {Donuts.Remote.IMessage} */
                const responseMsg = {
                    id: msg.id,
                    sender: this.id,
                    timestamp: Date.now(),
                    path: msg.path,
                    succeeded: succeeded,
                    body: response
                };

                Log.writeInfoAsync("{} REMOTE {,7} {} ~{,4:F0}ms => {}:{}", this.id, Action.Respond, responseMsg.id, responseMsg.timestamp - receivedTimestamp, msg.sender, msg.path);
                this.channelProxy.sendData(responseMsg);
            }
        };

        /**
         * @private
         * @param {Donuts.Remote.IChannelProxy} proxy
         * @return {void}
         */
        this.onClose = (proxy) => {
            this.emit("close", this);
        };

        /**
         * @private
         * @param {Donuts.Remote.IChannelProxy} proxy
         * @param {Error} err
         * @return {void}
         */
        this.onError = (proxy, err) => {
            this.emit("error", this, err);
        };

        this.channelProxy.setHandler("data", this.onMessageAsync);
        this.channelProxy.setHandler("close", this.onClose);
        this.channelProxy.setHandler("error", this.onError);
    }