Cursor.hasNext()
metoda je také "asynchronní", takže musíte await
to také. Totéž platí pro Cursor.next()
. Skutečné použití "smyčky" by proto mělo být while
:
async function dbanalyze(){
let cursor = db.collection('randomcollection').find()
while ( await cursor.hasNext() ) { // will return false when there are no more results
let doc = await cursor.next(); // actually gets the document
// do something, possibly async with the current document
}
}
Jak je uvedeno v komentářích, nakonec Cursor.hasNext()
vrátí false
když je kurzor skutečně vyčerpán, a Cursor.next()
je věc, která ve skutečnosti načítá každou hodnotu z kurzoru. Můžete udělat jiné struktury a break
smyčka, když hasNext()
je false
, ale přirozeněji se hodí k while
.
Ty jsou stále "asynchronní", takže musíte await
příslib usnesení o každém, a to byl hlavní fakt, který vám chyběl.
Stejně jako Cursor.map()
, pak vám pravděpodobně uniká to, že jej lze označit pomocí async
příznak také na poskytnuté funkci:
cursor.map( async doc => { // We can mark as async
let newDoc = await someAsyncMethod(doc); // so you can then await inside
return newDoc;
})
Ale pořád to ve skutečnosti chcete někde "iterovat", pokud vám to neprojde použitím .pipe()
do nějakého jiného výstupního cíle.
Také async/await
příznaky také vytvářejí Cursor.forEach()
"opět praktičtější" , protože jednou z běžných chyb je, že nelze jednoduše zpracovat „vnitřní“ asynchronní volání, ale s těmito příznaky to nyní můžete snadno udělat, i když je pravda, že musíte použijte zpětné volání, pravděpodobně to budete chtít zabalit do Promise :
await new Promise((resolve, reject) =>
cursor.forEach(
async doc => { // marked as async
let newDoc = await someAsyncMethod(doc); // so you can then await inside
// do other things
},
err => {
// await was respected, so we get here when done.
if (err) reject(err);
resolve();
}
)
);
Samozřejmě vždy existovaly způsoby, jak to aplikovat buď pomocí zpětných volání nebo implementací prostého Promise, ale to je "cukr" async/await
než ve skutečnosti vypadá mnohem čistěji.
NodeJS v10.x a ovladač MongoDB Node 3.1.xa vyšší
A oblíbená verze používá AsyncIterator
který je nyní povolen v NodeJS v10 a novějších. Je to mnohem čistší způsob iterace
async function dbanalyze(){
let cursor = db.collection('randomcollection').find()
for await ( let doc of cursor ) {
// do something with the current document
}
}
Které „svým způsobem“ se vrací k tomu, co otázka původně pokládala za použití for
smyčku, protože můžeme provést for-await-of
syntaxe zde podporuje iterovatelné, které podporuje správné rozhraní. A Cursor
podporuje toto rozhraní.
Pokud jste zvědaví, zde je seznam, který jsem před časem připravil, abych demonstroval různé techniky iterace kurzoru. Zahrnuje dokonce pouzdro pro asynchronní iterátory z funkce generátoru:
const Async = require('async'),
{ MongoClient, Cursor } = require('mongodb');
const testLen = 3;
(async function() {
let db;
try {
let client = await MongoClient.connect('mongodb://localhost/');
let db = client.db('test');
let collection = db.collection('cursortest');
await collection.remove();
await collection.insertMany(
Array(testLen).fill(1).map((e,i) => ({ i }))
);
// Cursor.forEach
console.log('Cursor.forEach');
await new Promise((resolve,reject) => {
collection.find().forEach(
console.log,
err => {
if (err) reject(err);
resolve();
}
);
});
// Async.during awaits cursor.hasNext()
console.log('Async.during');
await new Promise((resolve,reject) => {
let cursor = collection.find();
Async.during(
(callback) => Async.nextTick(() => cursor.hasNext(callback)),
(callback) => {
cursor.next((err,doc) => {
if (err) callback(err);
console.log(doc);
callback();
})
},
(err) => {
if (err) reject(err);
resolve();
}
);
});
// async/await allows while loop
console.log('async/await while');
await (async function() {
let cursor = collection.find();
while( await cursor.hasNext() ) {
let doc = await cursor.next();
console.log(doc);
}
})();
// await event stream
console.log('Event Stream');
await new Promise((end,error) => {
let cursor = collection.find();
for ( let [k,v] of Object.entries({ end, error, data: console.log }) )
cursor.on(k,v);
});
// Promise recursion
console.log('Promise recursion');
await (async function() {
let cursor = collection.find();
function iterate(cursor) {
return cursor.hasNext().then( bool =>
(bool) ? cursor.next().then( doc => {
console.log(doc);
return iterate(cursor);
}) : Promise.resolve()
)
}
await iterate(cursor);
})();
// Uncomment if node is run with async iteration enabled
// --harmony_async_iteration
console.log('Generator Async Iterator');
await (async function() {
async function* cursorAsyncIterator() {
let cursor = collection.find();
while (await cursor.hasNext() ) {
yield cursor.next();
}
}
for await (let doc of cursorAsyncIterator()) {
console.log(doc);
}
})();
// This is supported with Node v10.x and the 3.1 Series Driver
await (async function() {
for await (let doc of collection.find()) {
console.log(doc);
}
})();
client.close();
} catch(e) {
console.error(e);
} finally {
process.exit();
}
})();