Update forEach/Adds AsyncIterator (#87)

Switches the forEach implementation to be AsyncIterator instead.

Additionally, it changes the forEach implementation to accept a callback

fixes https://github.com/Azure/azure-cosmos-js/issues/73

fixes https://github.com/Azure/azure-cosmos-js/issues/71
This commit is contained in:
Christopher Anderson 2018-07-31 12:24:53 -07:00 коммит произвёл GitHub
Родитель a908af7cba
Коммит 5b5f33a824
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
4 изменённых файлов: 84 добавлений и 7 удалений

Просмотреть файл

@ -35,10 +35,67 @@ export class QueryIterator<T> {
this.resourceLink = resourceLink; this.resourceLink = resourceLink;
this.queryExecutionContext = this._createQueryExecutionContext(); this.queryExecutionContext = this._createQueryExecutionContext();
} }
/** /**
* Execute a provided function once per feed element. * Calls a specified callback for each item returned from the query.
* Runs serially; each callback blocks the next.
*
* @param callback Specified callback.
* First param is the result,
* second param (optional) is the current headers object state,
* third param (optional) is current index.
* No more callbacks will be called if one of them results false.
*
* @returns Promise<void> - you should await or .catch the Promise in case there are any errors
*
* @example Iterate over all databases
* ```typescript
* await client.databases.readAll().forEach((db, headers, index) => {
* console.log(`Got ${db.id} from forEach`);
* })
* ```
*/ */
public async *forEach(): AsyncIterable<Response<T>> { public async forEach(callback: (result: T, headers?: IHeaders, index?: number) => boolean | void): Promise<void> {
this.reset();
let index = 0;
while (this.queryExecutionContext.hasMoreResults()) {
const result = await this.queryExecutionContext.nextItem();
if (result.result === undefined) {
return;
}
if (callback(result.result, result.headers, index) === false) {
return;
} else {
++index;
}
}
}
/**
* Gets an async iterator that will yield results until completion.
*
* NOTE: AsyncIterators are a very new feature and you might need to
* use polyfils/etc. in order to use them in your code.
*
* If you're using TypeScript, you can use the following polyfill as long
* as you target ES6 or higher and are running on Node 6 or higher.
*
* ```typescript
* if (!Symbol || !Symbol.asyncIterator) {
* (Symbol as any).asyncIterator = Symbol.for("Symbol.asyncIterator");
* }
* ```
*
* @see QueryIterator.forEach for very similar functionality.
*
* @example Iterate over all databases
* ```typescript
* for await(const {result: db} in client.databases.readAll().getAsyncIterator()) {
* console.log(`Got ${db.id} from AsyncIterator`);
* }
* ```
*/
public async *getAsyncIterator(): AsyncIterable<Response<T>> {
this.reset(); this.reset();
while (this.queryExecutionContext.hasMoreResults()) { while (this.queryExecutionContext.hasMoreResults()) {
const result = await this.queryExecutionContext.nextItem(); const result = await this.queryExecutionContext.nextItem();

Просмотреть файл

@ -117,10 +117,10 @@ describe("NodeJS CRUD Tests", function() {
assert.equal(docs[2].id, resources.doc3.id); assert.equal(docs[2].id, resources.doc3.id);
}; };
const queryIteratorForEachTest = async function() { const queryIteratorAsyncIteratorTest = async function() {
const queryIterator = resources.container.items.readAll({ maxItemCount: 2 }); const queryIterator = resources.container.items.readAll({ maxItemCount: 2 });
let counter = 0; let counter = 0;
for await (const { result: doc } of queryIterator.forEach()) { for await (const { result: doc } of queryIterator.getAsyncIterator()) {
counter++; counter++;
if (counter === 1) { if (counter === 1) {
assert.equal(doc.id, resources.doc1.id, "first document should be doc1"); assert.equal(doc.id, resources.doc1.id, "first document should be doc1");
@ -133,6 +133,22 @@ describe("NodeJS CRUD Tests", function() {
assert(counter === 3, "iterator should have run 3 times"); assert(counter === 3, "iterator should have run 3 times");
}; };
const queryIteratorForEachTest = async function() {
const queryIterator = resources.container.items.readAll({ maxItemCount: 2 });
let counter = 0;
await queryIterator.forEach((item, headers, index) => {
counter++;
if (index === 0) {
assert.equal(item.id, resources.doc1.id, "first document should be doc1");
} else if (index === 1) {
assert.equal(item.id, resources.doc2.id, "second document should be doc2");
} else if (index === 2) {
assert.equal(item.id, resources.doc3.id, "third document should be doc3");
}
});
assert(counter === 3, "iterator should have run 3 times");
};
const queryIteratorNextAndMoreTest = async function() { const queryIteratorNextAndMoreTest = async function() {
const queryIterator = resources.container.items.readAll({ maxItemCount: 2 }); const queryIterator = resources.container.items.readAll({ maxItemCount: 2 });
assert.equal(queryIterator.hasMoreResults(), true); assert.equal(queryIterator.hasMoreResults(), true);
@ -186,7 +202,11 @@ describe("NodeJS CRUD Tests", function() {
await queryIteratorToArrayTest(); await queryIteratorToArrayTest();
}); });
it("nativeApi validate queryIterator iterator forEach name based", async function() { it("validate queryIterator asyncIterator", async function() {
await queryIteratorAsyncIteratorTest();
});
it("validate queryIterator forEach", async function() {
await queryIteratorForEachTest(); await queryIteratorForEachTest();
}); });

Просмотреть файл

@ -183,7 +183,7 @@ describe.skip("NodeJS Aggregate Query Tests", async function() {
const results: any[] = []; const results: any[] = [];
let callbackSingnalledEnd = false; let callbackSingnalledEnd = false;
// forEach uses callbacks still, so just wrap in a promise // forEach uses callbacks still, so just wrap in a promise
for await (const { result: item } of queryIterator.forEach()) { for await (const { result: item } of queryIterator.getAsyncIterator()) {
// if the previous invocation returned false, forEach must avoid invoking the callback again! // if the previous invocation returned false, forEach must avoid invoking the callback again!
assert.equal(callbackSingnalledEnd, false, "forEach called callback after the first false returned"); assert.equal(callbackSingnalledEnd, false, "forEach called callback after the first false returned");
results.push(item); results.push(item);

Просмотреть файл

@ -231,7 +231,7 @@ describe("Cross Partition", function() {
const results: any[] = []; const results: any[] = [];
let callbackSingnalledEnd = false; let callbackSingnalledEnd = false;
// forEach uses callbacks still, so just wrap in a promise // forEach uses callbacks still, so just wrap in a promise
for await (const { result: item } of queryIterator.forEach()) { for await (const { result: item } of queryIterator.getAsyncIterator()) {
// if the previous invocation returned false, forEach must avoid invoking the callback again! // if the previous invocation returned false, forEach must avoid invoking the callback again!
assert.equal(callbackSingnalledEnd, false, "forEach called callback after the first false returned"); assert.equal(callbackSingnalledEnd, false, "forEach called callback after the first false returned");
results.push(item); results.push(item);