diff --git a/packages/firestore/src/core/sync_engine.ts b/packages/firestore/src/core/sync_engine.ts index c67b7f70256..c1f26403486 100644 --- a/packages/firestore/src/core/sync_engine.ts +++ b/packages/firestore/src/core/sync_engine.ts @@ -684,7 +684,7 @@ export class SyncEngine implements RemoteSyncer, SharedClientStateSyncer { queryView.targetId ); this.limboDocumentRefs.removeReferencesForId(queryView.targetId); - await PersistencePromise.forEach(limboKeys.toArray(), limboKey => { + await PersistencePromise.forEach(limboKeys, limboKey => { return this.limboDocumentRefs .containsKey(null, limboKey) .next(isReferenced => { diff --git a/packages/firestore/src/local/persistence_promise.ts b/packages/firestore/src/local/persistence_promise.ts index 017fb028473..c79b555bc90 100644 --- a/packages/firestore/src/local/persistence_promise.ts +++ b/packages/firestore/src/local/persistence_promise.ts @@ -170,49 +170,54 @@ export class PersistencePromise { static waitFor( // tslint:disable-next-line:no-any Accept all Promise types in waitFor(). - all: Array> + all: Iterable> ): PersistencePromise { - const expectedCount = all.length; - if (expectedCount === 0) { - return PersistencePromise.resolve(); - } - - let resolvedCount = 0; + const it = all[Symbol.iterator](); return new PersistencePromise((resolve, reject) => { - for (const promise of all) { - promise.next( + let expectedCount = 0; + let resolvedCount = 0; + + let result = it.next(); + while (!result.done) { + ++expectedCount; + result.value.next( () => { ++resolvedCount; - if (resolvedCount === expectedCount) { + if (result.done && resolvedCount === expectedCount) { resolve(); } }, err => reject(err) ); + result = it.next(); + } + + if (resolvedCount === expectedCount) { + resolve(); } }); } - static map(all: Array>): PersistencePromise { + static map(all: Iterable>): PersistencePromise { const results: R[] = []; - const promises: Array> = []; - for (let i = 0; i < all.length; ++i) { - promises[i] = all[i].next(result => { - results[i] = result; - }); - } - return PersistencePromise.waitFor(promises).next(() => { - return results; - }); + return PersistencePromise.forEach(all, result => { + results.push(result); + return PersistencePromise.resolve(); + }).next(() => results); } static forEach( - elements: T[], + all: Iterable, callback: (T) => PersistencePromise ): PersistencePromise { + const it = all[Symbol.iterator](); + let p = PersistencePromise.resolve(); - for (const element of elements) { - p = p.next(() => callback(element)); + let result = it.next(); + while (!result.done) { + const value = result.value; + p = p.next(() => callback(value)); + result = it.next(); } return p; } diff --git a/packages/firestore/src/util/sorted_map.ts b/packages/firestore/src/util/sorted_map.ts index 2692b20253d..3bcdc64354e 100644 --- a/packages/firestore/src/util/sorted_map.ts +++ b/packages/firestore/src/util/sorted_map.ts @@ -168,6 +168,21 @@ export class SortedMap { getReverseIteratorFrom(key: K): SortedMapIterator { return new SortedMapIterator(this.root, key, this.comparator, true); } + + [Symbol.iterator](): Iterator> { + const it = this.getIterator(); + return { + next(): IteratorResult> { + if (it.hasNext()) { + return { done: false, value: it.getNext() }; + } else { + // The TypeScript typings don't allow `undefined` for Iterator, + // so we return an empty object instead. + return { done: true, value: {} as Entry }; + } + } + }; + } } // end SortedMap // An iterator over an LLRBNode. diff --git a/packages/firestore/src/util/sorted_set.ts b/packages/firestore/src/util/sorted_set.ts index 1c7da829016..55626775321 100644 --- a/packages/firestore/src/util/sorted_set.ts +++ b/packages/firestore/src/util/sorted_set.ts @@ -154,6 +154,21 @@ export class SortedSet { return 'SortedSet(' + result.toString() + ')'; } + [Symbol.iterator](): Iterator { + const it = this.data.getIterator(); + return { + next(): IteratorResult { + if (it.hasNext()) { + return { done: false, value: it.getNext().key }; + } else { + // The TypeScript typings don't allow `undefined` for Iterator, + // so we return an empty object instead. + return { done: true, value: {} as T }; + } + } + }; + } + private copy(data: SortedMap): SortedSet { const result = new SortedSet(this.comparator); result.data = data;