dolphindb 2.0.943 → 2.0.945
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/browser.d.ts +34 -13
- package/browser.js +208 -138
- package/browser.js.map +1 -1
- package/i18n/dict.json +3 -0
- package/index.d.ts +39 -16
- package/index.js +205 -135
- package/index.js.map +1 -1
- package/package.json +2 -2
- package/test.d.ts +3 -0
- package/test.js +232 -0
- package/test.js.map +1 -0
package/browser.d.ts
CHANGED
|
@@ -257,7 +257,7 @@ export declare class DdbObj<TValue extends DdbValue = DdbValue> {
|
|
|
257
257
|
/** 自动转换 js string, boolean 为 DdbObj */
|
|
258
258
|
static to_ddbobj(value: DdbObj | string | boolean): DdbObj;
|
|
259
259
|
/** 转换 js 数组为 DdbObj[] */
|
|
260
|
-
static to_ddbobjs(values:
|
|
260
|
+
static to_ddbobjs(values: (DdbObj | string | boolean)[]): DdbObj<DdbValue>[];
|
|
261
261
|
to_cols(): {
|
|
262
262
|
title: string;
|
|
263
263
|
dataIndex: string;
|
|
@@ -345,7 +345,7 @@ export declare class DdbVectorString extends DdbObj<string[]> {
|
|
|
345
345
|
constructor(strings: string[], name?: string);
|
|
346
346
|
}
|
|
347
347
|
export declare class DdbVectorAny extends DdbObj {
|
|
348
|
-
constructor(objs: (DdbObj | string | boolean
|
|
348
|
+
constructor(objs: (DdbObj | string | boolean)[], name?: string);
|
|
349
349
|
}
|
|
350
350
|
export declare class DdbVectorSymbol extends DdbObj<DdbSymbolExtendedValue> {
|
|
351
351
|
constructor(strings: string[], name?: string);
|
|
@@ -464,7 +464,7 @@ type DdbRpcType = 'script' | 'function' | 'variable' | 'connect';
|
|
|
464
464
|
export interface DdbRpcOptions {
|
|
465
465
|
script?: string;
|
|
466
466
|
func?: string;
|
|
467
|
-
args?: (DdbObj | string | boolean
|
|
467
|
+
args?: (DdbObj | string | boolean)[];
|
|
468
468
|
vars?: string[];
|
|
469
469
|
urgent?: boolean;
|
|
470
470
|
listener?: DdbMessageListener;
|
|
@@ -508,17 +508,26 @@ export declare class DDB {
|
|
|
508
508
|
python: boolean;
|
|
509
509
|
/** 是否为流数据连接,非流数据这个字段恒为 null Whether it is a streaming data connection, this field is always null for non-streaming data */
|
|
510
510
|
streaming: StreamingData;
|
|
511
|
+
/** 是否打印每个 rpc 的信息用于调试 */
|
|
512
|
+
verbose: boolean;
|
|
511
513
|
print_message_buffer: boolean;
|
|
512
514
|
print_object_buffer: boolean;
|
|
513
515
|
print_message: boolean;
|
|
514
516
|
parse_object: boolean;
|
|
515
517
|
pnode_run_defined: boolean;
|
|
518
|
+
/** 在 websocket 收到的第一个 error 时,
|
|
519
|
+
在 connect_websocket 的 on_error 回调中构造 DdbConnectionError 并保存到 DDB 对象上,
|
|
520
|
+
这个 error 的错误信息最准确
|
|
521
|
+
this.errored 为 true 时建议 throw this.error || new DdbConnectionError(this) */
|
|
522
|
+
error: DdbConnectionError;
|
|
516
523
|
/** DdbMessage listeners */
|
|
517
524
|
listeners: DdbMessageListener[];
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
525
|
+
/** 为所有 websocket 操作加锁,包括设置 this.on_message, this.on_error, websocket.send */
|
|
526
|
+
pwebsocket: import("xshell/utils.browser.js").Deferred<void>;
|
|
527
|
+
/** 为定义 pnode_run 函数加锁,避免重复定义 */
|
|
528
|
+
ppnode_run: import("xshell/utils.browser.js").Deferred<void>;
|
|
521
529
|
get connected(): boolean;
|
|
530
|
+
get errored(): boolean | DdbConnectionError;
|
|
522
531
|
/**
|
|
523
532
|
使用 WebSocket URL 初始化连接到 DolphinDB 的实例(不建立实际的网络连接)
|
|
524
533
|
Initialize an instance of DolphinDB Client using the WebSocket URL (without establishing an actual network connection)
|
|
@@ -530,6 +539,7 @@ export declare class DDB {
|
|
|
530
539
|
- password?: DolphinDB 登录密码,默认 `'123456'` DolphinDB password, default `'123456'`
|
|
531
540
|
- python?: 设置 python session flag,默认 `false` set python session flag, default `false`
|
|
532
541
|
- streaming?: 设置该选项后,该 WebSocket 连接只用于流数据 When this option is set, the WebSocket connection is only used for streaming data
|
|
542
|
+
- verbose?: 是否打印每个 rpc 的信息用于调试
|
|
533
543
|
|
|
534
544
|
@example
|
|
535
545
|
let ddb = new DDB('ws://127.0.0.1:8848')
|
|
@@ -548,12 +558,23 @@ export declare class DDB {
|
|
|
548
558
|
password?: string;
|
|
549
559
|
python?: boolean;
|
|
550
560
|
streaming?: StreamingParams;
|
|
561
|
+
verbose?: boolean;
|
|
551
562
|
});
|
|
552
563
|
private on_message;
|
|
553
564
|
private on_error;
|
|
554
|
-
/**
|
|
555
|
-
|
|
556
|
-
|
|
565
|
+
/** 调用后会确保和数据库的连接是正常的 (this.connected === true && this.errored === false),否则抛出错误
|
|
566
|
+
这个方法是幂等的,首次调用建立实际的 WebSocket 连接到 URL 对应的 DolphinDB,后续调用检查上面的条件
|
|
567
|
+
连接断开后禁止再次调用 connect 重连原有 ddb 对象,应该通过 new DDB() 的方式新建连接对象,原因是:
|
|
568
|
+
1. on_error 回调和某个 websocket 绑定了,不方便解绑后重新绑定
|
|
569
|
+
2. session 是有状态的,重连也无法恢复之前的状态
|
|
570
|
+
3. 断线后所有之前的 ddb.call, ddb.eval 都应该抛出连接错误
|
|
571
|
+
|
|
572
|
+
After calling, it will ensure that the connection with the database is normal (this.connected === true && this.errored === false), otherwise an error will be thrown
|
|
573
|
+
This method is idempotent, the first call establishes an actual WebSocket connection to the DolphinDB corresponding to the URL, and subsequent calls check the above conditions
|
|
574
|
+
After the connection is disconnected, it is forbidden to call connect again to reconnect the original ddb object. You should use new DDB() to create a new connection object because:
|
|
575
|
+
1. The on_error callback is bound to a certain websocket, and it is inconvenient to unbind and rebind
|
|
576
|
+
2. The session is stateful, and the previous state cannot be restored even after reconnection
|
|
577
|
+
3. After disconnection, all previous ddb.call, ddb.eval should throw a connection error */
|
|
557
578
|
connect(): Promise<void>;
|
|
558
579
|
get_rpc_options({ urgent, secondary, async: _async, pickle, clear, api, compress, cancellable, priority, parallelism, root_id, limit, }?: {
|
|
559
580
|
urgent?: boolean;
|
|
@@ -579,10 +600,10 @@ export declare class DDB {
|
|
|
579
600
|
limit?: boolean;
|
|
580
601
|
}): string;
|
|
581
602
|
disconnect(): void;
|
|
582
|
-
/** rpc through websocket (function/script/variable command)
|
|
603
|
+
/** (内部使用的方法) rpc through websocket (function/script/variable command)
|
|
583
604
|
未连接到 DDB 时调用会自动连接,连接断开时调用会抛出 DdbConnectionError
|
|
584
605
|
When the DDB is not connected, the call will be automatically connected. When the connection is disconnected, the call will throw the DdbConnectionError
|
|
585
|
-
- type: API 类型: 'script' | 'function' | 'variable'
|
|
606
|
+
- type: API 类型: 'script' | 'function' | 'variable' | 'connect'
|
|
586
607
|
- options:
|
|
587
608
|
- urgent?: 决定 `行为标识` 那一行字符串的取值(只适用于 script 和 function)
|
|
588
609
|
- vars?: type === 'variable' 时必传,variable 指令中待上传的变量名
|
|
@@ -630,7 +651,7 @@ export declare class DDB {
|
|
|
630
651
|
When it is false, the returned DdbObj only contains buffer and le without parsing,
|
|
631
652
|
so as to facilitate subsequent forwarding and serialization
|
|
632
653
|
*/
|
|
633
|
-
call<TResult extends DdbObj>(func: string, args?: (DdbObj | string | boolean
|
|
654
|
+
call<TResult extends DdbObj>(func: string, args?: (DdbObj | string | boolean)[], { urgent, node, nodes, func_type, add_node_alias, listener, parse_object, }?: {
|
|
634
655
|
urgent?: boolean;
|
|
635
656
|
node?: string;
|
|
636
657
|
nodes?: string[];
|
|
@@ -668,7 +689,7 @@ export interface DdbObjectMessage {
|
|
|
668
689
|
}
|
|
669
690
|
export interface DdbErrorMessage {
|
|
670
691
|
type: 'error';
|
|
671
|
-
data:
|
|
692
|
+
data: DdbDatabaseError;
|
|
672
693
|
}
|
|
673
694
|
export type DdbMessage = DdbPrintMessage | DdbObjectMessage | DdbErrorMessage;
|
|
674
695
|
export declare let ddb: DDB;
|
package/browser.js
CHANGED
|
@@ -1691,6 +1691,7 @@ export class DdbNanoTimeStamp extends DdbObj {
|
|
|
1691
1691
|
}
|
|
1692
1692
|
export class DdbBlob extends DdbObj {
|
|
1693
1693
|
constructor(value) {
|
|
1694
|
+
assert(value, t('new DdbBlob 不能传空的 value'));
|
|
1694
1695
|
super({
|
|
1695
1696
|
form: DdbForm.scalar,
|
|
1696
1697
|
type: DdbType.blob,
|
|
@@ -2154,6 +2155,8 @@ export class DdbConnectionError extends Error {
|
|
|
2154
2155
|
ddb;
|
|
2155
2156
|
constructor(ddb, error) {
|
|
2156
2157
|
super(error?.message || `${ddb.url} ${t('连接出错了,可能由于网络原因连接已被关闭,或服务器断开连接')}`, { cause: error });
|
|
2158
|
+
if (error)
|
|
2159
|
+
this.cause = error;
|
|
2157
2160
|
this.ddb = ddb;
|
|
2158
2161
|
}
|
|
2159
2162
|
}
|
|
@@ -2194,19 +2197,30 @@ export class DDB {
|
|
|
2194
2197
|
python = false;
|
|
2195
2198
|
/** 是否为流数据连接,非流数据这个字段恒为 null Whether it is a streaming data connection, this field is always null for non-streaming data */
|
|
2196
2199
|
streaming = null;
|
|
2200
|
+
/** 是否打印每个 rpc 的信息用于调试 */
|
|
2201
|
+
verbose = false;
|
|
2197
2202
|
// --- 内部选项, 状态
|
|
2198
2203
|
print_message_buffer = false;
|
|
2199
2204
|
print_object_buffer = false;
|
|
2200
2205
|
print_message = true;
|
|
2201
2206
|
parse_object = true;
|
|
2202
2207
|
pnode_run_defined = false;
|
|
2208
|
+
/** 在 websocket 收到的第一个 error 时,
|
|
2209
|
+
在 connect_websocket 的 on_error 回调中构造 DdbConnectionError 并保存到 DDB 对象上,
|
|
2210
|
+
这个 error 的错误信息最准确
|
|
2211
|
+
this.errored 为 true 时建议 throw this.error || new DdbConnectionError(this) */
|
|
2212
|
+
error;
|
|
2203
2213
|
/** DdbMessage listeners */
|
|
2204
2214
|
listeners = [];
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2215
|
+
/** 为所有 websocket 操作加锁,包括设置 this.on_message, this.on_error, websocket.send */
|
|
2216
|
+
pwebsocket = defer(null);
|
|
2217
|
+
/** 为定义 pnode_run 函数加锁,避免重复定义 */
|
|
2218
|
+
ppnode_run = defer(null);
|
|
2208
2219
|
get connected() {
|
|
2209
|
-
return this.websocket?.readyState === WebSocket.OPEN;
|
|
2220
|
+
return !this.error && this.websocket?.readyState === WebSocket.OPEN;
|
|
2221
|
+
}
|
|
2222
|
+
get errored() {
|
|
2223
|
+
return this.error || (this.websocket && this.websocket.readyState !== WebSocket.OPEN);
|
|
2210
2224
|
}
|
|
2211
2225
|
/**
|
|
2212
2226
|
使用 WebSocket URL 初始化连接到 DolphinDB 的实例(不建立实际的网络连接)
|
|
@@ -2219,6 +2233,7 @@ export class DDB {
|
|
|
2219
2233
|
- password?: DolphinDB 登录密码,默认 `'123456'` DolphinDB password, default `'123456'`
|
|
2220
2234
|
- python?: 设置 python session flag,默认 `false` set python session flag, default `false`
|
|
2221
2235
|
- streaming?: 设置该选项后,该 WebSocket 连接只用于流数据 When this option is set, the WebSocket connection is only used for streaming data
|
|
2236
|
+
- verbose?: 是否打印每个 rpc 的信息用于调试
|
|
2222
2237
|
|
|
2223
2238
|
@example
|
|
2224
2239
|
let ddb = new DDB('ws://127.0.0.1:8848')
|
|
@@ -2232,14 +2247,18 @@ export class DDB {
|
|
|
2232
2247
|
})
|
|
2233
2248
|
*/
|
|
2234
2249
|
constructor(url, options = {}) {
|
|
2250
|
+
const { searchParams, pathname } = new URL(location.href);
|
|
2235
2251
|
if (!url) {
|
|
2236
|
-
const
|
|
2237
|
-
const
|
|
2238
|
-
const
|
|
2239
|
-
|
|
2240
|
-
url = `${dev ? (_url.searchParams.get('tls') === '1' ? 'wss' : 'ws') : (location.protocol === 'https:' ? 'wss' : 'ws')}://${hostname}${port ? `:${port}` : ''}/`;
|
|
2252
|
+
const dev = pathname.endsWith('/console/');
|
|
2253
|
+
const hostname = searchParams.get('hostname') || (dev ? '127.0.0.1' : location.hostname);
|
|
2254
|
+
const port = searchParams.get('port') || (dev ? '8848' : location.port);
|
|
2255
|
+
url = `${dev ? (searchParams.get('tls') === '1' ? 'wss' : 'ws') : (location.protocol === 'https:' ? 'wss' : 'ws')}://${hostname}${port ? `:${port}` : ''}/`;
|
|
2241
2256
|
}
|
|
2242
2257
|
this.url = url;
|
|
2258
|
+
this.verbose = options.verbose === undefined ?
|
|
2259
|
+
searchParams.get('verbose') === '1'
|
|
2260
|
+
:
|
|
2261
|
+
options.verbose;
|
|
2243
2262
|
if (options.autologin !== undefined)
|
|
2244
2263
|
this.autologin = options.autologin;
|
|
2245
2264
|
if (options.username !== undefined)
|
|
@@ -2252,61 +2271,85 @@ export class DDB {
|
|
|
2252
2271
|
this.streaming = options.streaming;
|
|
2253
2272
|
}
|
|
2254
2273
|
on_message(buffer, websocket) {
|
|
2255
|
-
|
|
2274
|
+
throw t('这是在调用 this.rpc 之前默认的 on_message, 不应该被调用到,除非建立连接后 server 先推送了 message');
|
|
2256
2275
|
}
|
|
2257
|
-
on_error(
|
|
2276
|
+
on_error() {
|
|
2258
2277
|
// 这里的实现一定会被 connect, rpc 中的实现覆盖
|
|
2259
2278
|
}
|
|
2260
|
-
/**
|
|
2261
|
-
|
|
2262
|
-
|
|
2279
|
+
/** 调用后会确保和数据库的连接是正常的 (this.connected === true && this.errored === false),否则抛出错误
|
|
2280
|
+
这个方法是幂等的,首次调用建立实际的 WebSocket 连接到 URL 对应的 DolphinDB,后续调用检查上面的条件
|
|
2281
|
+
连接断开后禁止再次调用 connect 重连原有 ddb 对象,应该通过 new DDB() 的方式新建连接对象,原因是:
|
|
2282
|
+
1. on_error 回调和某个 websocket 绑定了,不方便解绑后重新绑定
|
|
2283
|
+
2. session 是有状态的,重连也无法恢复之前的状态
|
|
2284
|
+
3. 断线后所有之前的 ddb.call, ddb.eval 都应该抛出连接错误
|
|
2285
|
+
|
|
2286
|
+
After calling, it will ensure that the connection with the database is normal (this.connected === true && this.errored === false), otherwise an error will be thrown
|
|
2287
|
+
This method is idempotent, the first call establishes an actual WebSocket connection to the DolphinDB corresponding to the URL, and subsequent calls check the above conditions
|
|
2288
|
+
After the connection is disconnected, it is forbidden to call connect again to reconnect the original ddb object. You should use new DDB() to create a new connection object because:
|
|
2289
|
+
1. The on_error callback is bound to a certain websocket, and it is inconvenient to unbind and rebind
|
|
2290
|
+
2. The session is stateful, and the previous state cannot be restored even after reconnection
|
|
2291
|
+
3. After disconnection, all previous ddb.call, ddb.eval should throw a connection error */
|
|
2263
2292
|
async connect() {
|
|
2293
|
+
if (this.errored)
|
|
2294
|
+
throw this.error || new DdbConnectionError(this);
|
|
2264
2295
|
if (this.connected)
|
|
2265
2296
|
return;
|
|
2266
|
-
|
|
2267
|
-
const
|
|
2268
|
-
|
|
2297
|
+
// 准备进入 websocket 临界区
|
|
2298
|
+
const ptail = this.pwebsocket;
|
|
2299
|
+
let pwebsocket = this.pwebsocket = defer();
|
|
2300
|
+
// 避免 pwebsocket.reject 后出现 triggerUncaughtException
|
|
2301
|
+
pwebsocket.catch(() => { });
|
|
2302
|
+
// 上一个请求出现了 websocket 错误,那么直接抛出异常,且通过 reject 当前锁 (pwebsocket),安全退出临界区
|
|
2269
2303
|
try {
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
await this.call('login', [this.username, this.password], { urgent: true });
|
|
2304
|
+
await ptail;
|
|
2305
|
+
// 已进入 websocket 临界区
|
|
2306
|
+
}
|
|
2307
|
+
catch (error) {
|
|
2308
|
+
pwebsocket.reject(error);
|
|
2309
|
+
throw error;
|
|
2310
|
+
}
|
|
2311
|
+
if (this.errored) {
|
|
2312
|
+
const error = this.error || new DdbConnectionError(this);
|
|
2313
|
+
pwebsocket.reject(error);
|
|
2314
|
+
throw error;
|
|
2315
|
+
}
|
|
2316
|
+
if (this.connected) {
|
|
2317
|
+
pwebsocket.resolve();
|
|
2318
|
+
return;
|
|
2319
|
+
}
|
|
2320
|
+
this.on_error = () => {
|
|
2321
|
+
pwebsocket.reject(this.error /* 一定有,不需要再 || new DdbConnectionError(this) */);
|
|
2322
|
+
};
|
|
2323
|
+
try {
|
|
2324
|
+
this.websocket = await connect_websocket(this.url, {
|
|
2325
|
+
protocols: (() => {
|
|
2326
|
+
if (this.streaming)
|
|
2327
|
+
return 'streaming';
|
|
2328
|
+
if (this.python)
|
|
2329
|
+
return 'python';
|
|
2330
|
+
})(),
|
|
2331
|
+
on_message: (buffer, websocket) => {
|
|
2332
|
+
this.on_message(buffer, websocket);
|
|
2333
|
+
},
|
|
2334
|
+
on_error: error => {
|
|
2335
|
+
this.error ??= new DdbConnectionError(this, error);
|
|
2336
|
+
this.on_error();
|
|
2304
2337
|
}
|
|
2305
|
-
}
|
|
2338
|
+
});
|
|
2306
2339
|
}
|
|
2307
|
-
|
|
2308
|
-
|
|
2340
|
+
catch (error) {
|
|
2341
|
+
this.error ??= new DdbConnectionError(this, error);
|
|
2342
|
+
pwebsocket.reject(error);
|
|
2343
|
+
throw this.error;
|
|
2309
2344
|
}
|
|
2345
|
+
assert(this.connected);
|
|
2346
|
+
pwebsocket.resolve();
|
|
2347
|
+
// websocket 临界区结束
|
|
2348
|
+
await this.rpc('connect', {});
|
|
2349
|
+
if (this.autologin)
|
|
2350
|
+
await this.call('login', [this.username, this.password], { urgent: true });
|
|
2351
|
+
if (this.streaming)
|
|
2352
|
+
await this.subscribe();
|
|
2310
2353
|
}
|
|
2311
2354
|
get_rpc_options({ urgent = false, secondary = false, async: _async = false, pickle = false, clear = false, api = false, compress = false, cancellable = true, priority = urgent ? 8 : 4, parallelism = 8, root_id = '', limit, } = {}) {
|
|
2312
2355
|
let flag = 0;
|
|
@@ -2343,10 +2386,10 @@ export class DDB {
|
|
|
2343
2386
|
if (this.connected)
|
|
2344
2387
|
this.websocket.close(1000);
|
|
2345
2388
|
}
|
|
2346
|
-
/** rpc through websocket (function/script/variable command)
|
|
2389
|
+
/** (内部使用的方法) rpc through websocket (function/script/variable command)
|
|
2347
2390
|
未连接到 DDB 时调用会自动连接,连接断开时调用会抛出 DdbConnectionError
|
|
2348
2391
|
When the DDB is not connected, the call will be automatically connected. When the connection is disconnected, the call will throw the DdbConnectionError
|
|
2349
|
-
- type: API 类型: 'script' | 'function' | 'variable'
|
|
2392
|
+
- type: API 类型: 'script' | 'function' | 'variable' | 'connect'
|
|
2350
2393
|
- options:
|
|
2351
2394
|
- urgent?: 决定 `行为标识` 那一行字符串的取值(只适用于 script 和 function)
|
|
2352
2395
|
- vars?: type === 'variable' 时必传,variable 指令中待上传的变量名
|
|
@@ -2354,19 +2397,27 @@ export class DDB {
|
|
|
2354
2397
|
- parse_object?: 在本次 rpc 期间设置 parse_object, 结束后恢复原有
|
|
2355
2398
|
为 false 时返回的 DdbObj 仅含有 buffer 和 le,不做解析,以便后续转发、序列化 */
|
|
2356
2399
|
async rpc(type, options) {
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
const { script, func, vars = [], urgent, listener, } = options;
|
|
2362
|
-
let { args = [] } = options;
|
|
2400
|
+
// 保留调用栈信息
|
|
2401
|
+
let error = new DdbDatabaseError('', this, type, options);
|
|
2402
|
+
await this.connect();
|
|
2403
|
+
const { script, func, args: _args = [], vars = [], urgent, listener, } = options;
|
|
2363
2404
|
if (func === 'pnode_run' && !this.pnode_run_defined) {
|
|
2364
|
-
//
|
|
2365
|
-
const ptail = this.
|
|
2366
|
-
|
|
2367
|
-
|
|
2405
|
+
// 准备进入 pnode_run 临界区,保证并发调用 rpc 时只定义一次 pnode_run
|
|
2406
|
+
const ptail = this.ppnode_run;
|
|
2407
|
+
let ppnode_run = this.ppnode_run = defer();
|
|
2408
|
+
// 避免 ppnode_run.reject 后出现 triggerUncaughtException
|
|
2409
|
+
ppnode_run.catch(() => { });
|
|
2410
|
+
// 如果之前的定义失败了,再定义一次也会失败,直接抛出之前的错误,且安全地退出临界区
|
|
2368
2411
|
try {
|
|
2369
|
-
|
|
2412
|
+
await ptail;
|
|
2413
|
+
// 已进入 pnode_run 临界区
|
|
2414
|
+
}
|
|
2415
|
+
catch (error) {
|
|
2416
|
+
ppnode_run.reject(error);
|
|
2417
|
+
throw error;
|
|
2418
|
+
}
|
|
2419
|
+
if (!this.pnode_run_defined)
|
|
2420
|
+
try {
|
|
2370
2421
|
await this.eval(this.python ?
|
|
2371
2422
|
'def pnode_run (nodes, func_name, args, add_node_alias):\n' +
|
|
2372
2423
|
' nargs = size(args)\n' +
|
|
@@ -2402,86 +2453,108 @@ export class DDB {
|
|
|
2402
2453
|
' add_node_alias\n' +
|
|
2403
2454
|
' )\n' +
|
|
2404
2455
|
'}\n', { urgent: true });
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2456
|
+
this.pnode_run_defined = true;
|
|
2457
|
+
ppnode_run.resolve();
|
|
2458
|
+
}
|
|
2459
|
+
catch (error) {
|
|
2460
|
+
// 这次失败了,之后的执行肯定也会失败
|
|
2461
|
+
ppnode_run.reject(error);
|
|
2462
|
+
throw error;
|
|
2463
|
+
}
|
|
2409
2464
|
}
|
|
2410
2465
|
// this 上的当前配置需要在 message 到达后使用,先保存起来
|
|
2411
|
-
const
|
|
2412
|
-
|
|
2413
|
-
// 临界区:保证多个 rpc 并发时形成 promise 链
|
|
2466
|
+
const _listeners = [...this.listeners].reverse();
|
|
2467
|
+
// websocket 临界区:保证多个 rpc 并发时形成 promise 链
|
|
2414
2468
|
// ddb 世界观:需要等待上一个 rpc 结果从 server 返回之后才能发起下一个调用
|
|
2415
2469
|
// 违反世界观可能造成:
|
|
2416
2470
|
// 1. 并发多个请求只返回第一个结果(阻塞,需后续请求疏通)
|
|
2417
2471
|
// 2. windows 下 ddb server 返回多个相同的结果
|
|
2418
|
-
const ptail = this.
|
|
2419
|
-
|
|
2472
|
+
const ptail = this.pwebsocket;
|
|
2473
|
+
let pwebsocket = this.pwebsocket = defer();
|
|
2474
|
+
// 避免 pwebsocket.reject 后出现 triggerUncaughtException
|
|
2475
|
+
pwebsocket.catch(() => { });
|
|
2476
|
+
// 上一个请求出现了 websocket 错误,那么直接抛出异常,且通过 reject 当前锁 (pwebsocket),安全退出临界区
|
|
2420
2477
|
try {
|
|
2421
2478
|
await ptail;
|
|
2479
|
+
// 已进入临界区,只有一个 rpc 函数调用运行到这里,可以独占 this.on_message 然后写 WebSocket
|
|
2422
2480
|
}
|
|
2423
|
-
catch {
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
presult.reject(new DdbConnectionError(this));
|
|
2427
|
-
return presult;
|
|
2481
|
+
catch (error) {
|
|
2482
|
+
pwebsocket.reject(error);
|
|
2483
|
+
throw error;
|
|
2428
2484
|
}
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2485
|
+
// 既然上一个请求没有出现 websocket error,且函数开头已经调用了 await this.connect() 检查过,
|
|
2486
|
+
// 这里也乐观的认为 this.connected && !this.errored 为 true
|
|
2487
|
+
return new Promise((resolve, reject) => {
|
|
2488
|
+
this.on_error = () => {
|
|
2489
|
+
pwebsocket.reject(this.error /* 这里一定有了 this.error, 不需要再 || new DdbConnectionError(this) */);
|
|
2490
|
+
reject(this.error);
|
|
2491
|
+
};
|
|
2492
|
+
this.on_message = buffer => {
|
|
2493
|
+
try {
|
|
2494
|
+
const buf = new Uint8Array(buffer);
|
|
2495
|
+
if (this.print_message_buffer)
|
|
2496
|
+
console.log(buf);
|
|
2497
|
+
const message = this.parse_message(buf, error);
|
|
2498
|
+
listener?.(message, this);
|
|
2499
|
+
for (const listener of _listeners)
|
|
2500
|
+
listener(message, this);
|
|
2501
|
+
const { type, data } = message;
|
|
2502
|
+
switch (type) {
|
|
2503
|
+
case 'print':
|
|
2504
|
+
if (this.print_message)
|
|
2505
|
+
console.log(data);
|
|
2506
|
+
break;
|
|
2507
|
+
case 'object':
|
|
2508
|
+
pwebsocket.resolve();
|
|
2509
|
+
resolve(data);
|
|
2510
|
+
break;
|
|
2511
|
+
case 'error':
|
|
2512
|
+
pwebsocket.resolve();
|
|
2513
|
+
reject(data);
|
|
2514
|
+
break;
|
|
2515
|
+
}
|
|
2516
|
+
}
|
|
2517
|
+
catch (error) {
|
|
2518
|
+
pwebsocket.resolve();
|
|
2519
|
+
// 这里的错误并非 websocket 错误,而是 rpc 错误
|
|
2520
|
+
reject(error);
|
|
2521
|
+
}
|
|
2522
|
+
};
|
|
2523
|
+
const args = DdbObj.to_ddbobjs(_args);
|
|
2524
|
+
const command = this.enc.encode((() => {
|
|
2442
2525
|
switch (type) {
|
|
2443
|
-
case '
|
|
2444
|
-
if (this.
|
|
2445
|
-
console.log(
|
|
2446
|
-
return
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
case '
|
|
2451
|
-
|
|
2452
|
-
|
|
2526
|
+
case 'function':
|
|
2527
|
+
if (this.verbose)
|
|
2528
|
+
console.log(func + args.map(arg => arg.toString()).join(', ').bracket());
|
|
2529
|
+
return 'function\n' +
|
|
2530
|
+
`${func}\n` +
|
|
2531
|
+
`${args.length}\n` +
|
|
2532
|
+
`${Number(DDB.le_client)}\n`;
|
|
2533
|
+
case 'script':
|
|
2534
|
+
if (this.verbose)
|
|
2535
|
+
console.log(script);
|
|
2536
|
+
return 'script\n' +
|
|
2537
|
+
script;
|
|
2538
|
+
case 'variable':
|
|
2539
|
+
if (this.verbose)
|
|
2540
|
+
for (let i = 0; i < vars.length; i++)
|
|
2541
|
+
console.log(`${vars[i]} = ${args[i].toString()}`);
|
|
2542
|
+
return 'variable\n' +
|
|
2543
|
+
`${vars.join(',')}\n` +
|
|
2544
|
+
`${vars.length}\n` +
|
|
2545
|
+
`${Number(DDB.le_client)}\n`;
|
|
2546
|
+
case 'connect':
|
|
2547
|
+
if (this.verbose)
|
|
2548
|
+
console.log('connect()');
|
|
2549
|
+
return 'connect\n';
|
|
2453
2550
|
}
|
|
2454
|
-
}
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
switch (type) {
|
|
2462
|
-
case 'function':
|
|
2463
|
-
return 'function\n' +
|
|
2464
|
-
`${func}\n` +
|
|
2465
|
-
`${args.length}\n` +
|
|
2466
|
-
`${Number(DDB.le_client)}\n`;
|
|
2467
|
-
case 'script':
|
|
2468
|
-
return 'script\n' +
|
|
2469
|
-
script;
|
|
2470
|
-
case 'variable':
|
|
2471
|
-
return 'variable\n' +
|
|
2472
|
-
`${vars.join(',')}\n` +
|
|
2473
|
-
`${vars.length}\n` +
|
|
2474
|
-
`${Number(DDB.le_client)}\n`;
|
|
2475
|
-
case 'connect':
|
|
2476
|
-
return 'connect\n';
|
|
2477
|
-
}
|
|
2478
|
-
})());
|
|
2479
|
-
this.websocket.send(concat([
|
|
2480
|
-
this.enc.encode(`API2 ${this.sid} ${command.length} / ${this.get_rpc_options({ urgent })}\n`),
|
|
2481
|
-
command,
|
|
2482
|
-
...args.map((arg) => arg.pack())
|
|
2483
|
-
]));
|
|
2484
|
-
return presult;
|
|
2551
|
+
})());
|
|
2552
|
+
this.websocket.send(concat([
|
|
2553
|
+
this.enc.encode(`API2 ${this.sid} ${command.length} / ${this.get_rpc_options({ urgent })}\n`),
|
|
2554
|
+
command,
|
|
2555
|
+
...args.map(arg => arg.pack())
|
|
2556
|
+
]));
|
|
2557
|
+
});
|
|
2485
2558
|
}
|
|
2486
2559
|
/** eval script through websocket (script command)
|
|
2487
2560
|
- script?: 执行的脚本 Script to execute
|
|
@@ -2633,9 +2706,6 @@ export class DDB {
|
|
|
2633
2706
|
}
|
|
2634
2707
|
/** 内部的流订阅方法 Internal stream subscription method */
|
|
2635
2708
|
async subscribe() {
|
|
2636
|
-
await this.rpc('connect', {});
|
|
2637
|
-
if (this.autologin)
|
|
2638
|
-
await this.call('login', [this.username, this.password]);
|
|
2639
2709
|
console.log(t('订阅流表成功') + ', colnames:',
|
|
2640
2710
|
// string[3](['time', 'stock', 'price'])
|
|
2641
2711
|
this.streaming.colnames = (await this.call('publishTable', [
|