-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathjson-rpc.js
More file actions
93 lines (82 loc) · 2.11 KB
/
json-rpc.js
File metadata and controls
93 lines (82 loc) · 2.11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import rand from '@mfelements/rand'
class NamedError extends Error{
constructor(message){
super(message);
this.name = Object.getPrototypeOf(this).constructor.name || 'Error';
}
}
class ErrorWithCode extends NamedError{
constructor({ code, message }){
super(message);
this.code = code;
}
}
export class JSONRPCError extends ErrorWithCode{}
export class NetworkError extends ErrorWithCode{}
const resolvers = {};
const currentQueues = {};
function timeoutErr(){
return new NetworkError({ code: -1, message: 'Timeout exceeded' });
}
function initQueue(url){
currentQueues[url] = [];
currentQueues[url].timeout = 0;
}
function rejectAll(queue, reason){
queue.forEach(({ id }) => {
const { reject } = resolvers[id];
delete resolvers[id];
reject(reason);
});
}
function sendQueue(url){
const queue = currentQueues[url];
initQueue(url);
const { timeout } = queue;
const requestData = queue.length === 1 ? queue[0] : queue;
const controller = new AbortController;
const { signal } = controller;
let rejectHandler;
if(timeout) rejectHandler = setTimeout(() => {
rejectAll(queue, timeoutErr());
controller.abort();
}, timeout);
let res;
try{
res = await fetch(url, {
method: 'POST',
body: JSON.stringify(requestData),
signal,
}).then(v => {
clearTimeout(rejectHandler);
return v.json();
});
} catch(e){
rejectAll(queue, e);
}
for(const r of Array.isArray(res) ? res : [res]){
const { resolve, reject } = resolvers[r.id];
delete resolvers[r.id];
if(r.error) reject(JSONRPCError(r.error));
else resolve(r.result);
}
}
export default function request(url, method, params, timeout = null){
return new Promise((resolve, reject) => {
const id = rand(2);
resolvers[id] = { resolve, reject };
if(!currentQueues[url]) initQueue(url);
currentQueues[url].push({
jsonrpc: '2.0',
method,
params,
id,
});
if(timeout !== null){
if(timeout > currentQueues[url].timeout) currentQueues[url].timeout = timeout;
setTimeout(() => reject(timeoutErr()), timeout);
}
clearTimeout(currentQueues[url].handler);
currentQueues[url].handler = setTimeout(() => sendQueue(url))
})
}