2017-12-15 13 views
0

経由近くのmongo DB接続は、私が考えなどNodejs、コールバック

実行プログラム非同期(async)、コールバックに問題がある:MongoDBのに

  1. 接続します。
  2. URLを作成する - https://example.com + locArrayから部品を追加する。
  3. それぞれにリクエストを送信します。
  4. データをmongo dbに保存します。
  5. 接続を閉じます。

問題:

接続はjsonDataFromApiに最後の行に閉鎖された場合は
  • - DBに保存された各要求からすべてのデータの前に、「サーバインスタンスプールが破壊された」

  • だからcallback(db)が別の場所に送られた - closeMongoDb

  • しかし、エラーが現れた

    "未定義の 'close'プロパティを読み取れません。私が思う

は、問題は非同期で、そこに非同期性に問題は間違いなくあるなど

const MongoClient = require('mongodb').MongoClient; 
     const Array = require('node-array'); 
     const request = require('request'); 

     var locationArray = [ 
      'location1', 
      'location2', 
      'location3', 
      'location4' 
     ]; 

     var dataFromLocApi = (loc, callback) => { 
      request({ 
      url: `https://example.com/${loc}`, 
      json: true 
      }, (error, response, body) => { 
      if (error){ 
      callback('Error connection to url.'); 
      } else{ 
      callback(undefined, body.result); 
      } 
     }); 
     }; 

     var jsonDataFromApi = (urldb, callback) => { 
     MongoClient.connect(urldb, (err, db) => { 
      if (err) { 
      console.log('MongoDb connection error.'); 
      } 
      console.log('MongoDb - connected.'); 
      locationArray.forEachAsync(function(loc, index, arr) { 
      dataFromLocApi(loc, (errorMessage, results) => { 
       if (errorMessage) { 
       console.log(errorMessage); 
       } else { 
       console.log(JSON.stringify(results, undefined, 2)); 
       db.collection('testCollection').insert(results, function(error, record) { 
        if (error) 
        throw error; 
        console.log("data saved"); 
       }); 
       } 
      }); 

      }, function() { 
      console.log('complete'); 
      }); 
     callback(db); 
     }); 
     } 

var closeMongoDb = (urldb, callback) => { 
    jsonDataFromApi(urldb, (error, db) => { 
     if (error){ 
     callback('Close connection - failure'); 
     } else{ 
     db.close(); 
     console.log('MongoDb connections was closed.'); 
    } 
    }); 
    } 

    closeMongoDb('mongodb://127.0.0.1:27017/testDb', (err, db) => { 

     console.log('DONE'); 
    }); 
+0

また、関数closeMongoDbのコードを貼り付けてもらえますか? –

+0

すべてのコードですが、closeMongoDbは関数ではありません。 – profiler

+0

それは何ですか? –

答えて

1

コールバックを送っています。 db.close()に電話をかける前に、アイテムが処理されるのを待っているわけではありません。

また、定義した関数の意味が不明です。たとえば、関数closeMongoDbは、基本的にDBを終了する必要があります。しかし、ここでは他の仕事があります:データを取り出し、後でDBを閉じます。

また、node-arrayの代わりにasyncモジュールを使用して、最後の問題を解決するようです。

私はコードをリファクタリングしました。私のコメントを読んでください。私はできるだけ明確にしようとしました。

const MongoClient = require("mongodb").MongoClient; 
 
const request = require("request"); 
 
// We are going to use the async module 
 
// This is a classical module to handle async behavior. 
 
const async = require("async"); 
 

 
// As you can see this function accepts a callback 
 
// If there is an error connecting to the DB 
 
// it passes it up to the caller via callback(err) 
 
// This is a general pattern 
 
const connectToDb = function(urldb, callback) { 
 
    MongoClient.connect(urldb, (err, db) => { 
 
     if (err) { 
 
      console.log("MongoDb connection error."); 
 
      callback(err); 
 
      return; 
 
     } 
 

 
     // If everything is OK, pass the db as a data to the caller. 
 
     callback(undefined, db); 
 
    }); 
 
}; 
 

 
// This method fetches the data for a single location. 
 
// The logic with errors/data is absolutely the same. 
 
const getData = (loc, callback) => { 
 
    request(
 
     { 
 
      url: `https://example.com/${loc}`, 
 
      json: true 
 
     }, 
 
     (error, response, body) => { 
 
      if (error) { 
 
       callback("Error connection to url."); 
 
       return; 
 
      } 
 

 
      callback(undefined, body.result); 
 
     } 
 
    ); 
 
}; 
 

 
// This function goes over each location, pulls the data and saves it to the DB 
 
// Last parameter is a callback, I called it allDataFetchedCb to make it clear 
 
// that we are calling it after ALL the locations have been processed 
 
// And everything is saved to the DB. 
 
const saveDataFromLocations = function(locations, db, allDataFetchedCb) { 
 
    // First param here is an array of items 
 
    // The second one is an async function that we want to execute for each item 
 
    // When a single item is processed we call the callback. I named it 'locProcessedCB' 
 
    // So it's clear what happens. 
 
    // The third parameter is a callback that is going to be called when ALL the items 
 
    // have been processed. 
 
    async.each(
 
     locations, 
 
     function(loc, locProcessedCb) { 
 
      getData(loc, (apiErr, results) => { 
 
       if (apiErr) { 
 
        console.log(apiErr); 
 
        // Well, we couldn't process the item, pass the error up. 
 
        locProcessedCb(apiErr); 
 
        return; 
 
       } 
 

 
       console.log(
 
        `Obtained the data from the api: ${JSON.stringify(
 
         results, 
 
         undefined, 
 
         2 
 
        )}` 
 
       ); 
 

 
       db.collection("testCollection").insert(results, function(dbError) { 
 
        if (dbError) { 
 
         // Also an error, we couldn't process the item. 
 
         locProcessedCb(dbError); 
 
         return; 
 
        } 
 

 
        // Ok the item is processed without errors, after calling this 
 
        // So we tell the async.each function: ok, good, go on and process the next one. 
 
        locProcessedCb(); 
 
       }); 
 
      }); 
 
     }, 
 
     function(err) { 
 
      // We gonna get here after all the items have been processed or any error happened. 
 
      if (err) { 
 
       allDataFetchedCb(err); 
 
       return; 
 
      } 
 

 
      console.log("All the locations have been processed."); 
 

 
      // All good, passing the db object up. 
 
      allDataFetchedCb(undefined, db); 
 
     } 
 
    ); 
 
}; 
 

 
// This function is an entry point. 
 
// It calls all the above functions one by one. 
 
const getDataAndCloseDb = function(urldb, locations, callback) { 
 
    //Well, let's connect. 
 
    connectToDb(urldb, (err, db) => { 
 
     if (err) { 
 
      callback(err); 
 
      return; 
 
     } 
 

 
     // Now let's get everything. 
 
     saveDataFromLocations(locations, db, (err, db) => { 
 
      if (err) { 
 
       callback(err); 
 
       return; 
 
      } 
 

 
      // If somehow there is no db object, or no close method we wanna know about it. 
 
      if (!db || !db.close) { 
 
       callback(new Error("Unable to close the DB Connection.")); 
 
      } 
 

 
      // Closing the DB. 
 
      db.close(err => { 
 
       // If there's no error err === undefined or null 
 
       // So this call is equal to callback(undefined); 
 
       callback(err); 
 
      }); 
 
     }); 
 
    }); 
 
}; 
 

 
const locationArray = ["location1", "location2", "location3", "location4"]; 
 

 
// Finally calling the function, passing all needed data inside. 
 
getDataAndCloseDb("mongodb://127.0.0.1:27017/testDb", locationArray, err => { 
 
    if (err) { 
 
     console.error(
 
      `Unable to fetch the data due to the following reason: ${err}` 
 
     ); 
 
     return; 
 
    } 
 

 
    console.log("Done successfully."); 
 
});

私はURLを持っていないと私はそれを自分で試してみて、必要に応じてデバッグしてくださいなど、このコードを実行されませんでした。

+0

ありがとう、完璧ですが、私はコールバックを理解して一般的な機能を作成していると思います。私のためのレッスン 一つ時々疑問、我々は ' 'のconstのgetData =(LOC、コールバック)=>を書く(すべてのリソースに応じて) しかし、別の時間:。。 'のconst connectToDb =機能(urldb、コールバック)'。 'cons X = function {}'と 'const X =()=> {}'の間に大きな違いがありますか?私がそれを変更すると、behavioそれは同じです。私が問題になるのは、関数を使うべきとき、矢印関数 '=>'のときです。 – profiler

+0

@profilerここでは、 '(x)=> x * 2'のような矢印関数を、通常の'関数 'の省略形として使用しました。実際、それらの間には違いがあります。しかし、この特定のケースでは、まったく同じように動作します。あなたは、「矢印機能の使い方」のようなグーグルをすることができます。または、あなたのためのビデオはここにあります:https://youtu.be/J85lRtO_yjY –

+0

あなたの助けをありがとう。 2番目の関数を非同期に追加しようとしていますが、それでも "結果"は利用できません。私の質問に上記の回答を追加しました。 1つの非同期function.each関数から別の非同期関数に応答を送信する可能性がありますか?チェックしていただきありがとうございます。 – profiler

0

@ antonio-narkevich ありがとうございますnodejsに関する大きな情報と大きな助けがあります。

私はいくつかのエクササイズをしようとしていますが、すべてを正しく理解しているかどうか確認したいと思います。しかし、再び、いくつかの問題があります。

  1. 例えば、のgetData関数からの結果は:

    body.result.information [1] = "情報1"、body.result.information [2] = "情報2"

    https://example.com/ $ {LOC}/$ {情報} - >https://example.com/location1/information1

は、新たな機能を追加しました:

const getDataInformation = (loc, info, callback) => { 
    request(
     { 
      url: `https://example.com/${loc}/${info}`, 
      json: true 
     }, 
     (error, response, body) => { 
      if (error) { 
       callback("Error connection to url."); 
       return; 
      } 

      callback(undefined, body.result); 
     } 
    ); 
}; 

そして、sync関数 - saveDataFromLocation - が変更されました。 getDataInformationを:結果にDBから新しい同様の機能、saceDataInformationを追加しました

const saveDataFromLocations = function(locations, db, allDataFetchedCb) { 
    async.each(
     locations, 
     function(loc, locProcessedCb) { 
      getData(loc, (apiErr, results) => { 
       if (apiErr) { 
        console.log(apiErr); 
        locProcessedCb(apiErr); 
        return; 
       } 

       console.log(
        `Obtained the data from the api: ${JSON.stringify(
         results, 
         undefined, 
         2 
        )}` 
       ); 

       db.collection("testCollectionInformation").insert(results, function(dbError) { 
        if (dbError) { 
         locProcessedCb(dbError); 
         return; 
        } 

        locProcessedCb(); 
       }); 
      }); 
     }, 
     function(err) { 
      if (err) { 
       allDataFetchedCb(err); 
       return; 
      } 

      console.log("All the locations have been processed."); 

      allDataFetchedCb(undefined, results); 
     } 
    ); 
}; 

(のgetDataから)が、使用されます - 私は、コールバックを変更しなければなりません。そして、私は一般的な関数から実行します:

const getDataAndCloseDb = function(urldb, locations, callback) { 
    connectToDb(urldb, (err, db) => { 
     if (err) { 
      callback(err); 
      return; 
     } 

     saveDataFromLocations(locations, db, (err, results) => { 
      if (err) { 
       callback(err); 
       return; 
      } 


     }); 

     saveDataInformation(locations, results, (err, db) => { 
      if (err) { 
       callback(err); 
       return; 
      } 


      if (!db || !db.close) { 
       callback(new Error("Unable to close the DB Connection. Second edition.")); 
      } 


      db.close(err => { 

       callback(err); 
      }); 
     }); 
    }); 
}; 

結果は上記の関数で定義されていません。しかし、コールバックsaveDataFromLocationsで送信する必要があります...事前にありがとうございます。

+0

私は問題は正しいとは確信していませんが、saveDataFromLocation関数の結果をsaveDataInformationに渡したい場合は、saveDataFromLocationのコールバック内でsaveDataInformation *を呼び出す必要があります。 (もし後(エラー){...})SDFInformationに –

+0

結果SDFLocations: 'saveDataInformation(その結果、DB、(errorInf、resultsInformation)=> { \t \t \t \t \t IF(errorInf){ コンソール。log(errorInf); repoProcessedCb(errorInf); リターン; } console.log( "プロジェクト:" + results + "が取得されました。 repoProcessedCb(resultsInformation); ; \t \t \t}); \t \t \t \t \t \t はconsole.log( "すべての場所が処理されています。"); allDataFetchedCb(未定義、db); ' しかし、saveDataFromLocationsはasync.eachからも実行する必要がありますか? – profiler

+0

上記の関数は、saveDataFromLocationsのコールバックに追加されました。 getDataInfomationからデータを取得するのにまだ問題があります。私は両方の関数を非同期から実行する必要がありますか? 2番目の - saveDataInfornationもasync.jsから実行する必要があり、私の例のようにsaveDataFromLocationsと非常に似ていますか? – profiler

関連する問題