sql >> Databasteknik >  >> NoSQL >> MongoDB

Asynkron marköriteration med asynkron deluppgift

Cursor.hasNext() Metoden är också "asynkron", så du måste await det också. Detsamma gäller Cursor.next() . Därför borde den faktiska "loop"-användningen verkligen vara en 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
  }

}

Som noterats i kommentarerna, så småningom Cursor.hasNext() returnerar false när markören faktiskt är utarmad, och Cursor.next() är det som faktiskt hämtar varje värde från markören. Du kan göra andra strukturer och break slingan när hasNext() är false , men det lämpar sig mer naturligt för en while .

Dessa är fortfarande "asynkrona", så du måste await löfteslösningen på var och en, och det var det huvudsakliga faktumet du saknade.

När det gäller Cursor.map() , då missar du förmodligen punkten att den kan markeras med en async flagga på den tillhandahållna funktionen också:

 cursor.map( async doc => {                   // We can mark as async
    let newDoc = await someAsyncMethod(doc);  // so you can then await inside
    return newDoc;
 })

Men du vill fortfarande "iterate" det någonstans, om du inte kan komma undan med att använda .pipe() till någon annan utdatadestination.

Även async/await flaggor gör också Cursor.forEach() "mer praktiskt igen" , eftersom det är en vanlig brist var att inte bara kunna hantera ett "inre" asynkront samtal, men med dessa flaggor kan du nu göra det med lätthet, men visserligen eftersom du måste använd en återuppringning, du vill antagligen slå in detta i ett löfte :

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();
    }
  )
);

Naturligtvis har det alltid funnits sätt att tillämpa detta med antingen callbacks eller vanliga Promise-implementationer, men det är "sockret" i async/await än faktiskt gör att det här ser mycket renare ut.

NodeJS v10.x och MongoDB Node drivrutin 3.1.x och uppåt

Och favoritversionen använder AsyncIterator som nu är aktiverat i NodeJS v10 och uppåt. Det är ett mycket renare sätt att iterera

async function dbanalyze(){

  let cursor = db.collection('randomcollection').find()
  for await ( let doc of cursor ) {
    // do something with the current document
  }    
}

Vilket "på ett sätt" kommer tillbaka till vad frågan ursprungligen ställdes om att använda en for loop eftersom vi kan göra for-await-of syntax här för att stödja iterable som stöder rätt gränssnitt. Och Cursor stöder detta gränssnitt.

Om du är nyfiken, här är en lista som jag gjorde för en tid sedan för att demonstrera olika marköriterationstekniker. Den innehåller till och med ett fodral för Async Iterators från en generatorfunktion:

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();
  }

})();



  1. MongoDB-CR-autentisering misslyckades

  2. Få dokument med taggar i listan, sorterade efter totalt antal matchningar

  3. Inaktivera Transparent Huge Pages från Kubernetes

  4. 6 viktiga Redis-övervakningsstatistik du behöver titta på