Skip to content

Commit 84f74e9

Browse files
committed
added new utils: each, map (these chunk iterations and yield to event loop every chunk) and generatePushID (for ref().push()
1 parent 95e7fad commit 84f74e9

File tree

1 file changed

+145
-10
lines changed

1 file changed

+145
-10
lines changed

lib/utils/index.js

Lines changed: 145 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
// modeled after base64 web-safe chars, but ordered by ASCII
2+
const PUSH_CHARS = '-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz';
3+
4+
const DEFAULT_CHUNK_SIZE = 50;
5+
6+
// internal promise handler
7+
const _handler = (resolve, reject, err, resp) => {
8+
// resolve / reject after events etc
9+
setImmediate(() => {
10+
if (err) return reject(err);
11+
return resolve(resp);
12+
});
13+
};
14+
15+
116
/**
217
* Makes an objects keys it's values
318
* @param object
@@ -17,16 +32,6 @@ export function reverseKeyValues(object) {
1732
export function noop() {
1833
}
1934

20-
21-
// internal promise handler
22-
const _handler = (resolve, reject, err, resp) => {
23-
// resolve / reject after events etc
24-
setImmediate(() => {
25-
if (err) return reject(err);
26-
return resolve(resp);
27-
});
28-
};
29-
3035
/**
3136
* Wraps a native module method to support promises.
3237
* @param fn
@@ -41,3 +46,133 @@ export function promisify(fn, NativeModule) {
4146
});
4247
};
4348
}
49+
50+
51+
/**
52+
* Delays chunks based on sizes per event loop.
53+
* @param collection
54+
* @param chunkSize
55+
* @param operation
56+
* @param callback
57+
* @private
58+
*/
59+
function _delayChunk(collection, chunkSize, operation, callback) {
60+
const length = collection.length;
61+
const iterations = Math.ceil(length / chunkSize);
62+
63+
// noinspection ES6ConvertVarToLetConst
64+
let thisIteration = 0;
65+
66+
setImmediate(function next() {
67+
const start = thisIteration * chunkSize;
68+
const _end = start + chunkSize;
69+
const end = _end >= length ? length : _end;
70+
const result = operation(collection.slice(start, end), start, end);
71+
72+
if (thisIteration++ > iterations) {
73+
callback(null, result);
74+
} else {
75+
setImmediate(next);
76+
}
77+
});
78+
}
79+
80+
/**
81+
* Async each with optional chunk size limit
82+
* @param array
83+
* @param chunkSize
84+
* @param iterator
85+
* @param cb
86+
*/
87+
export function each(array, chunkSize, iterator, cb) {
88+
if (typeof chunkSize === 'function') {
89+
cb = iterator;
90+
iterator = chunkSize;
91+
chunkSize = DEFAULT_CHUNK_SIZE;
92+
}
93+
94+
_delayChunk(array, chunkSize, (slice, start) => {
95+
for (let ii = 0, jj = slice.length; ii < jj; ii += 1) {
96+
iterator(slice[ii], start + ii);
97+
}
98+
}, cb);
99+
}
100+
101+
/**
102+
* Async map with optional chunk size limit
103+
* @param array
104+
* @param chunkSize
105+
* @param iterator
106+
* @param cb
107+
* @returns {*}
108+
*/
109+
export function map(array, chunkSize, iterator, cb) {
110+
if (typeof chunkSize === 'function') {
111+
cb = iterator;
112+
iterator = chunkSize;
113+
chunkSize = DEFAULT_CHUNK_SIZE;
114+
}
115+
116+
const result = [];
117+
_delayChunk(array, chunkSize, (slice, start) => {
118+
for (let ii = 0, jj = slice.length; ii < jj; ii += 1) {
119+
result.push(iterator(slice[ii], start + ii, array));
120+
}
121+
return result;
122+
}, () => cb(result));
123+
}
124+
125+
126+
// timestamp of last push, used to prevent local collisions if you push twice in one ms.
127+
let lastPushTime = 0;
128+
129+
// we generate 72-bits of randomness which get turned into 12 characters and appended to the
130+
// timestamp to prevent collisions with other clients. We store the last characters we
131+
// generated because in the event of a collision, we'll use those same characters except
132+
// "incremented" by one.
133+
const lastRandChars = [];
134+
135+
/**
136+
* Generate a firebase id - for use with ref().push(val, cb) - e.g. -KXMr7k2tXUFQqiaZRY4'
137+
* @param serverTimeOffset - pass in server time offset from native side
138+
* @returns {string}
139+
*/
140+
export function generatePushID(serverTimeOffset = 0) {
141+
const timeStampChars = new Array(8);
142+
let now = new Date().getTime() + serverTimeOffset;
143+
const duplicateTime = (now === lastPushTime);
144+
145+
lastPushTime = now;
146+
147+
for (let i = 7; i >= 0; i -= 1) {
148+
timeStampChars[i] = PUSH_CHARS.charAt(now % 64);
149+
now = Math.floor(now / 64);
150+
}
151+
152+
if (now !== 0) throw new Error('We should have converted the entire timestamp.');
153+
154+
let id = timeStampChars.join('');
155+
156+
if (!duplicateTime) {
157+
for (let i = 0; i < 12; i += 1) {
158+
lastRandChars[i] = Math.floor(Math.random() * 64);
159+
}
160+
} else {
161+
// if the timestamp hasn't changed since last push,
162+
// use the same random number, but increment it by 1.
163+
let i;
164+
for (i = 11; i >= 0 && lastRandChars[i] === 63; i -= 1) {
165+
lastRandChars[i] = 0;
166+
}
167+
168+
lastRandChars[i] += 1;
169+
}
170+
171+
for (let i = 0; i < 12; i++) {
172+
id += PUSH_CHARS.charAt(lastRandChars[i]);
173+
}
174+
175+
if (id.length !== 20) throw new Error('Length should be 20.');
176+
177+
return id;
178+
}

0 commit comments

Comments
 (0)