sql >> Database >  >> NoSQL >> MongoDB

Asynchrone cursoriteratie met asynchrone subtaak

De Cursor.hasNext() methode is ook "asynchroon", dus u moet await dat ook. Hetzelfde geldt voor Cursor.next() . Daarom zou het daadwerkelijke "loop"-gebruik eigenlijk een while . moeten zijn :

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
  }

}

Zoals opgemerkt in de opmerkingen, uiteindelijk Cursor.hasNext() retourneert false wanneer de cursor daadwerkelijk leeg is, en de Cursor.next() is het ding dat eigenlijk elke waarde van de cursor ophaalt. Je zou andere structuren kunnen doen en break de lus wanneer hasNext() is false , maar het leent zich natuurlijker voor een while .

Deze zijn nog steeds "async", dus u moet await de belofte resolutie op elk, en dat was het belangrijkste feit dat je miste.

Wat betreft Cursor.map() , dan mis je waarschijnlijk het punt dat het kan worden gemarkeerd met een async vlag ook op de voorziene functie:

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

Maar je wilt dat nog steeds ergens "herhalen", tenzij je weg kunt komen met het gebruik van .pipe() naar een andere uitvoerbestemming.

Ook de async/await vlaggen maken ook Cursor.forEach() "weer praktischer" , omdat het een veelvoorkomende fout was dat je niet eenvoudig een "innerlijke" asynchrone oproep kon afhandelen, maar met deze vlaggen kun je dit nu gemakkelijk doen, hoewel je weliswaar moet gebruik een terugbelverzoek, u wilt dit waarschijnlijk in een belofte verpakken:

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

Natuurlijk zijn er altijd manieren geweest om dit toe te passen met callbacks of gewone Promise-implementaties, maar het is de "suiker" van async/await dan dit er in werkelijkheid veel schoner uitziet.

NodeJS v10.x en MongoDB Node-stuurprogramma 3.1.x en hoger

En de favoriete versie gebruikt AsyncIterator die nu is ingeschakeld in NodeJS v10 en hoger. Het is een veel schonere manier om te herhalen

async function dbanalyze(){

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

Welke "in zekere zin" komt terug op de vraag die oorspronkelijk werd gesteld over het gebruik van een for loop omdat we de for-await-of . kunnen doen syntaxis hier voor ondersteunende iterable die de juiste interface ondersteunt. En de Cursor ondersteunt deze interface.

Als je nieuwsgierig bent, hier is een lijst die ik enige tijd geleden heb gemaakt om verschillende cursor-iteratietechnieken te demonstreren. Het bevat zelfs een case voor Async Iterators van een generatorfunctie:

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. Dynamisch pad instellen in de redis.conf met behulp van de omgevingsvariabele

  2. Moet ik redis gebruiken om een ​​groot aantal binaire bestanden op te slaan?

  3. MongoDB Tekst Zoeken EN meerdere zoekwoorden

  4. Hoe de verbinding met Mongodb op de juiste manier opnieuw te gebruiken in de toepassing en modules van NodeJs