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
+
1
16
/**
2
17
* Makes an objects keys it's values
3
18
* @param object
@@ -17,16 +32,6 @@ export function reverseKeyValues(object) {
17
32
export function noop ( ) {
18
33
}
19
34
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
-
30
35
/**
31
36
* Wraps a native module method to support promises.
32
37
* @param fn
@@ -41,3 +46,133 @@ export function promisify(fn, NativeModule) {
41
46
} ) ;
42
47
} ;
43
48
}
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