MongoDB
 sql >> Baza danych >  >> NoSQL >> MongoDB

Asynchroniczna iteracja kursora z asynchronicznym podzadaniem

Cursor.hasNext() metoda jest również "asynchroniczna", więc musisz await to również. To samo dotyczy Cursor.next() . Dlatego rzeczywiste użycie "pętli" powinno być 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 zauważono w komentarzach, ostatecznie Cursor.hasNext() zwróci false kiedy kursor jest faktycznie wyczerpany, a Cursor.next() jest rzeczą, która faktycznie pobiera każdą wartość z kursora. Możesz zrobić inne struktury i break pętla, gdy hasNext() jest false , ale bardziej naturalnie nadaje się do while .

Są one nadal „asynchroniczne”, więc musisz await rozwiązanie obietnicy na każdym z nich i to był główny fakt, którego ci brakowało.

Co do Cursor.map() , prawdopodobnie brakuje Ci punktu, w którym można go oznaczyć za pomocą async flagę również na dostarczonej funkcji:

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

Ale nadal chcesz to gdzieś "iterować", chyba że możesz uciec z użyciem .pipe() do innego miejsca docelowego wyjścia.

Również async/await flagi również powodują, że Cursor.forEach() „znowu bardziej praktyczny” , ponieważ jedną z powszechnych wad jest niemożność obsługi „wewnętrznego” wywołania asynchronicznego, ale dzięki tym flagom możesz to zrobić teraz z łatwością, choć trzeba przyznać, że musisz użyj wywołania zwrotnego, prawdopodobnie chcesz umieścić to w obietnicy :

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

Oczywiście zawsze istniały sposoby na zastosowanie tego za pomocą wywołań zwrotnych lub zwykłych implementacji Promise, ale jest to „cukier” async/await niż w rzeczywistości sprawia, że ​​wygląda to o wiele czyściej.

NodeJS v10.x i sterownik MongoDB Node 3.1.x i nowsze

A ulubiona wersja używa AsyncIterator który jest teraz włączony w NodeJS v10 i nowszych. To znacznie czystszy sposób iteracji

async function dbanalyze(){

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

Które „w pewnym sensie” wraca do tego, co pierwotnie zadano pytanie, dotyczące używania for pętla, ponieważ możemy wykonać for-await-of tutaj składnia obsługuje iterację, która obsługuje poprawny interfejs. Oraz Cursor obsługuje ten interfejs.

Jeśli jesteście ciekawi, oto lista, którą przygotowałem jakiś czas temu, aby zademonstrować różne techniki iteracji kursora. Zawiera nawet skrzynkę na iteratory asynchroniczne z funkcji generatora:

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. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. MongoDB — Zapytanie o ostatni element tablicy?

  2. MongoDB - jak zapytać o zagnieżdżony element w kolekcji?

  3. Aktualizacja Mongoose nie aktualizuje się:{ ok:0, n:0, nModified:0 }

  4. Wykorzystanie buforowanych danych podczas sortowania przepełnienia przekracza wewnętrzny limit

  5. Konwertuj ciąg na ObjectID w MongoDB