-
Notifications
You must be signed in to change notification settings - Fork 3k
/
Copy pathSequentialQueue.js
131 lines (109 loc) · 3.55 KB
/
SequentialQueue.js
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import _ from 'underscore';
import Onyx from 'react-native-onyx';
import * as PersistedRequests from '../actions/PersistedRequests';
import * as NetworkStore from './NetworkStore';
import ONYXKEYS from '../../ONYXKEYS';
import * as ActiveClientManager from '../ActiveClientManager';
import * as Request from '../Request';
let resolveIsReadyPromise;
let isReadyPromise = new Promise((resolve) => {
resolveIsReadyPromise = resolve;
});
// Resolve the isReadyPromise immediately so that the queue starts working as soon as the page loads
resolveIsReadyPromise();
let isSequentialQueueRunning = false;
let currentRequest = null;
/**
* This method will get any persisted requests and fire them off in sequence to retry them.
*
* @returns {Promise}
*/
function process() {
const persistedRequests = PersistedRequests.getAll();
// This sanity check is also a recursion exit point
if (NetworkStore.isOffline() || _.isEmpty(persistedRequests)) {
return Promise.resolve();
}
const task = _.reduce(persistedRequests, (previousRequest, request) => previousRequest.then(() => {
currentRequest = Request.processWithMiddleware(request, true);
return currentRequest;
}), Promise.resolve());
// Do a recursive call in case the queue is not empty after processing the current batch
return task.then(process);
}
function flush() {
if (isSequentialQueueRunning) {
return;
}
// ONYXKEYS.PERSISTED_REQUESTS is shared across clients, thus every client/tab will have a copy
// It is very important to only process the queue from leader client otherwise requests will be duplicated.
if (!ActiveClientManager.isClientTheLeader()) {
return;
}
isSequentialQueueRunning = true;
// Reset the isReadyPromise so that the queue will be flushed as soon as the request is finished
isReadyPromise = new Promise((resolve) => {
resolveIsReadyPromise = resolve;
});
// Ensure persistedRequests are read from storage before proceeding with the queue
const connectionID = Onyx.connect({
key: ONYXKEYS.PERSISTED_REQUESTS,
callback: () => {
Onyx.disconnect(connectionID);
process()
.finally(() => {
isSequentialQueueRunning = false;
resolveIsReadyPromise();
currentRequest = null;
});
},
});
}
/**
* @returns {Boolean}
*/
function isRunning() {
return isSequentialQueueRunning;
}
// Flush the queue when the connection resumes
NetworkStore.onReconnection(flush);
/**
* @param {Object} request
*/
function push(request) {
// Add request to Persisted Requests so that it can be retried if it fails
PersistedRequests.save([request]);
// If we are offline we don't need to trigger the queue to empty as it will happen when we come back online
if (NetworkStore.isOffline()) {
return;
}
// If the queue is running this request will run once it has finished processing the current batch
if (isSequentialQueueRunning) {
isReadyPromise.then(flush);
return;
}
flush();
}
/**
* @returns {Promise}
*/
function getCurrentRequest() {
if (currentRequest === null) {
return Promise.resolve();
}
return currentRequest;
}
/**
* Returns a promise that resolves when the sequential queue is done processing all persisted write requests.
* @returns {Promise}
*/
function waitForIdle() {
return isReadyPromise;
}
export {
flush,
getCurrentRequest,
isRunning,
push,
waitForIdle,
};