@lcap/nasl
Version:
NetEase Application Specific Language
227 lines (220 loc) • 7.32 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
const uuid_1 = require('uuid');
/**
* 用于两个 window 之间通信
* 除了基本的发送消息,提供了如命令式等更多方法
*/
class Messager {
options;
requests;
constructor(options) {
this.options = Object.assign({
protocol: 'vusion',
sender: 'platform',
logLevel: 'info',
timeout: 30000,
onlySend: false,
getSender() {
return window.parent;
},
getReceiver() {
return window;
},
handleMessage(message) {
// handle
},
}, options);
this.requests = new Map();
this.onMessageReceived = this.onMessageReceived.bind(this);
this.onDataReceived = this.onDataReceived.bind(this);
if (!this.options.onlySend) {
const receiver = this.options.getReceiver.call(this.options.context);
if (receiver.addEventListener)
receiver.addEventListener('message', this.onMessageReceived);
else if (receiver.addListener)
receiver.addListener('message', this.onDataReceived);
}
}
destroy() {
!this.options.onlySend && this.options.getReceiver.call(this.options.context).removeEventListener('message', this.onMessageReceived);
}
/**
* 发送信息
* 所有下面方法的基础方法
* 比较原生,直接给目标 window 中 postMessage
*/
sendMessage(message) {
const logLevel = message.logLevel || this.options.logLevel;
if (logLevel === 'info') {
// const str = JSON.stringify(message);
// console.info(`[messager]`, str);
}
const sender = this.options.getSender.call(this.options.context);
if (typeof window !== 'undefined' && sender instanceof Window)
sender.postMessage(message, '*');
else
sender.postMessage(message);
}
/**
* 发送数据
* 包装了协议和发送者。Window 的 message 可能来自不同地方,统一格式区分。
* @param data 数据
*/
sendData(data) {
return this.sendMessage({
protocol: this.options.protocol,
sender: this.options.sender,
type: 'send',
data,
});
}
/**
* 发送命令
* 直接发送一个命令
* 包装了协议和发送者。Window 的 message 可能来自不同地方,统一格式区分。
* @param command 命令名
* @param args 参数
*/
sendCommand(command, ...args) {
return this.sendMessage({
protocol: this.options.protocol,
sender: this.options.sender,
type: 'send',
command,
args,
});
}
/**
* 请求信息
* 与 send 系列的区别是接收返回数据
*/
requestMessage(message, options) {
if (options) {
Object.assign(message, options);
}
this.sendMessage(message);
return new Promise((res, rej) => {
const timeoutTimer = setTimeout(() => {
this.requests.delete(message.id);
rej(Object.assign({ error: 'Timeout' }, message));
}, this.options.timeout || 2000);
const resolve = (data) => {
// 需要主动把定时器卸载掉,避免内存泄露
this.requests.delete(message.id);
clearTimeout(timeoutTimer);
res(data);
};
const reject = (e) => {
// 需要主动把定时器卸载掉,避免内存泄露
this.requests.delete(message.id);
clearTimeout(timeoutTimer);
rej(e);
};
this.requests.set(message.id, Object.assign({ res: resolve, rej: reject }, message));
});
}
/**
* 请求数据
* 与 sendData 系列的区别是接收返回数据
*/
requestData(data) {
const message = {
id: (0, uuid_1.v4)(),
protocol: this.options.protocol,
sender: this.options.sender,
type: 'request',
data,
};
return this.requestMessage(message);
}
/**
* 请求命令
* 与 sendCommand 系列的区别是接收返回数据
*/
requestCommand(command, ...args) {
const message = {
id: (0, uuid_1.v4)(),
protocol: this.options.protocol,
sender: this.options.sender,
type: 'request',
command,
args,
};
return this.requestMessage(message);
}
/**
* 内置处理接收的 MessageEvent 事件
* @override
*/
onMessageReceived(e) {
if (!e.data)
return;
this.onDataReceived(e.data);
}
count = 0;
/**
* 内置处理接收的数据
* @override
*/
onDataReceived(data) {
this.count++;
if (this.count === 5) {
this.count = 0;
// 预留时间做 minor gc,减少峰值内存占用
(async () => await new Promise(resolve => setTimeout(resolve, 30)))();
}
if (data.protocol !== this.options.protocol)
return;
if (data.type === 'response') {
const message = this.requests.get(data.id);
if (message) {
message.error ? message.rej(data.error) : message.res(data.result);
}
return;
}
if (data.command) {
const func = this.options.context[data.command];
if (!func)
// 函数不存在时不 response
return;
let res;
if (typeof func === 'function') {
res = func.call(this.options.context, ...data.args, data.id);
} else {
res = func;
}
if (data.type === 'request') {
return Promise.resolve(res)
.then((result) => {
this.sendMessage({
id: data.id,
protocol: this.options.protocol,
sender: this.options.sender,
logLevel: data.logLevel,
type: 'response',
command: data.command,
result,
});
})
.catch((error) => {
console.error(error);
this.sendMessage({
id: data.id,
protocol: this.options.protocol,
sender: this.options.sender,
logLevel: data.logLevel,
type: 'response',
command: data.command,
error,
});
});
} else {
return;
}
}
this.options.handleMessage(data);
}
}
exports.default = Messager;
//# sourceMappingURL=Messager.js.map