sql >> データベース >  >> NoSQL >> MongoDB

非同期サブタスクを使用した非同期カーソルの反復

    Cursor.hasNext() メソッドも「非同期」であるため、await必要があります それも。 Cursor.next()についても同じことが言えます 。したがって、実際の「ループ」の使用法は、実際には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
      }
    
    }
    

    コメントに記載されているように、最終的にはCursor.hasNext() falseを返します カーソルが実際に使い果たされたとき、およびCursor.next() カーソルから各値を実際に取得しているものです。他の構造を実行してbreakすることができます hasNext()の場合のループ falseです 、ただし、より自然にwhileに適しています 。

    これらはまだ「非同期」であるため、await必要があります それぞれの約束の決議、そしてそれはあなたが逃した主な事実でした。

    Cursor.map()について 、それならおそらくasyncでマークできるという点を見逃しているでしょう 提供された関数にもフラグを立てます:

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

    ただし、.pipe()を使用して回避できない限り、実際にはどこかで「繰り返し」たいと思うでしょう。 他の出力先へ。

    また、async/await フラグはCursor.forEach()も作成します 「またもっと実用的」 、これは一般的な欠陥の1つであるため、「内部」非同期呼び出しを単純に処理できないことでしたが、これらのフラグを使用すると、必須であるため、簡単に処理できるようになりました。 コールバックを使用します。おそらくこれを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();
        }
      )
    );
    

    もちろん、これをコールバックまたはプレーンなPromise実装のいずれかで適用する方法は常にありますが、これはasync/awaitの「砂糖」です。 実際にこれをはるかにきれいに見せます。

    NodeJSv10.xおよびMongoDBノードドライバー3.1.x以降

    そして、お気に入りのバージョンはAsyncIteratorを使用しています これは、NodeJSv10以降で有効になりました。これは、反復するためのはるかにクリーンな方法です

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

    どの「ある意味で」 forの使用に関して最初に尋ねられた質問に戻ります for-await-ofを実行できるのでループします ここでの構文は、正しいインターフェースをサポートするiterableをサポートします。そしてCursor このインターフェースをサポートしています。

    好奇心旺盛な方のために、さまざまなカーソル反復手法を示すために、少し前に作成したリストを示します。ジェネレーター関数からの非同期イテレーターのケースも含まれています:

    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. Python/PILを使用して画像をredisに保存する方法

    3. タイムエクスプレスとredisセッションの有効期限

    4. GridFSを使用してNode.jsとMongooseを使用して画像を保存する方法