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) {
1732export 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