sql >> Database teknologi >  >> NoSQL >> MongoDB

Asynkron cursor iteration med asynkron underopgave

Cursor.hasNext() metoden er også "asynkron", så du skal await det også. Det samme gælder for Cursor.next() . Derfor burde den faktiske "loop"-brug virkelig være 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 nævnt i kommentarerne, til sidst Cursor.hasNext() vil returnere false når markøren faktisk er udtømt, og Cursor.next() er den ting, der rent faktisk henter hver værdi fra markøren. Du kunne lave andre strukturer og break løkken, når hasNext() er false , men det egner sig mere naturligt til et while .

Disse er stadig "asynkrone", så du skal await løfteopløsningen på hver, og det var det vigtigste faktum, du manglede.

Med hensyn til Cursor.map() , så mangler du sandsynligvis det punkt, at det kan markeres med en async flag også på den medfølgende funktion:

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

Men du vil faktisk stadig gerne "iterere" det et eller andet sted, medmindre du kan slippe afsted med at bruge .pipe() til en anden outputdestination.

Også async/await flag laver også Cursor.forEach() "mere praktisk igen" , da det er en almindelig fejl ikke blot at kunne håndtere et "indre" asynkront opkald, men med disse flag kan du nu gøre det med lethed, dog indrømmet da du skal bruge et tilbagekald, vil du sandsynligvis pakke dette ind i et 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();
    }
  )
);

Selvfølgelig har der altid været måder at anvende dette på med enten tilbagekald eller almindelige Promise-implementeringer, men det er "sukkeret" i async/await end faktisk får dette til at se meget renere ud.

NodeJS v10.x og MongoDB Node driver 3.1.x og nyere

Og favoritversionen bruger AsyncIterator som nu er aktiveret i NodeJS v10 og opefter. Det er en meget renere måde at iterere på

async function dbanalyze(){

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

Hvilket "på en måde" vender tilbage til det spørgsmålet oprindeligt stillede om at bruge en for loop, da vi kan udføre for-await-of syntaks her for understøtter iterable, som understøtter den korrekte grænseflade. Og Cursor understøtter denne grænseflade.

Hvis du er nysgerrig, er her en liste, som jeg lavede for noget tid siden for at demonstrere forskellige markør-iterationsteknikker. Det inkluderer endda et etui til Async Iterators fra 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. Hvordan adskiller redis instansen med flere brugere, der kører på samme server?

  2. C#/.NET-klient til Redis

  3. Hvordan øger man et felt i mongodb?

  4. Fremtrædende træk ved MapReduce – Vigtigheden af ​​MapReduce