sql >> Databáze >  >> NoSQL >> MongoDB

Asynchronní iterace kurzoru s asynchronní dílčí úlohou

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

})();



  1. Vysoká dostupnost s Redis Sentinel:Připojení k Redis Master/Slave sadám

  2. změna typu mongodb na pole

  3. Redis Managed Pub/Sub Server

  4. Co je Express.js?