2017-12-15 1 views
0

를 통해 가까운 몽고 DB를 연결 나는 생각 등Nodejs, 콜백

실행 프로그램을 비동기 콜백의 문제를 가지고 : MongoDB를에

  1. 에 연결합니다.
  2. URL 만들기 - https://example.com + locArray에서 파트 추가.
  3. 요청 보내기 (각 요청).
  4. 데이터를 mongo db에 저장하십시오.
  5. 연결을 닫습니다.

문제 :

연결이 jsonDataFromApi에서 마지막 줄에 폐쇄 된 경우
  • - dB로 저장된 각 요청의 모든 데이터 전에 "서버 인스턴스 풀은 파괴되었다"

  • 그래서 callback(db)을 다른 장소로 보냈습니다 - closeMongoDb

  • 오류가 나타났습니다

    " '정의되지 않은'닫기 '속성을 읽을 수 없습니다. 내가 생각

는 문제가 비동기로,이 비동기에 문제가 확실히있다 등

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 모듈을 사용하는 것이 좋습니다. 다른 하나는 다른 문제를 해결하는 것 같습니다.

코드를 리팩토링했습니다. 내 의견을 읽어주십시오. 가능한 한 명확하게하려고 노력했습니다. I 등의 URL을하지 않는

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."); 
 
});

나는 필요에 따라서 직접 디버깅을 시도하십시오이 코드를 실행하지 않았다.

+0

고맙습니다. 이제 콜백을 이해하고 일반적인 기능을 만들었습니다. 'async.js'도 감사합니다. 매우 유용하고 훌륭한 설명, 훌륭한 것입니다. 한 가지 질문, 때로는 (모든 자료에 따라) 우리는 의''const getData = (loc, callback) =>''.''const connectToDb = function (urldb, callback)''이라고 부릅니다. 'cons X = function {}'과'const X =() => {}'사이에 큰 차이가 있습니까? 내가 그것을 바꾸면, 행동 그건 같아. 화살표 함수'=>'가 필요할 때 함수를 사용해야 할 때 문제가됩니다. – profiler

+0

@profiler 여기에서'(x) => x * 2 '와 같은 화살표 함수를 일반 함수의 약자로 사용했습니다. 실제로, 그들 사이에 차이가 있습니다. 그러나이 특별한 경우에는 정확히 동일하게 작동합니다. '화살표 기능 사용법'과 같은 인터넷 검색을 할 수 있습니다. 또는 비디오는 다음과 같습니다. https://youtu.be/J85lRtO_yjY –

+0

도움을 주셔서 감사합니다. 비동기에 두 번째 함수를 추가하려고하지만 여전히 "결과"를 사용할 수 없습니다. 위 질문에 대한 답을 추가했습니다. 하나의 비동기 function.each 함수에서 다른 하나의 응답으로 응답을 보낼 수 있습니까? 확인하고 도움을 주셔서 감사합니다. – profiler

0

@ antonio-narkevich 고맙습니다. nodejs에 대한 유용한 정보와 큰 도움.

나는 몇 가지 훈련을하고 있는데, 모든 것을 제대로 이해했는지 확인하고 싶습니다. 그러나, 다시, 약간 문제가 있으십시오.

  1. 예를 들어, GetData의 함수의 결과이다 :

    body.result.information [1] = "정보 1"body.result.information [2] = "INFORMATION2"

    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 (errorInf) { 콘솔 경우.log (errorInf); repoProcessedCb (errorInf); 반환; } console.log ("프로젝트 :"+ results + "을 (를) 가져 왔습니다."); repoProcessedCb (resultsInformation); \t \t \t}); \t \t \t \t \t \t CONSOLE.LOG ("모든 사항을 처리하고있다."); allDataFetchedCb (undefined, db); ' 그러나 saveDataFromLocations도 async.each에서 실행해야합니까? – profiler

+0

위 함수가 saveDataFromLocations의 콜백에 추가되었습니다. 여전히 getDataInfomation에서 데이터를 가져 오는 데 문제가 있습니다. 두 함수 모두 async에서 실행해야합니까? 두 번째 것 - saveDataInfornation은 async.js에서도 실행되어야하며, 내 예제와 마찬가지로 saveDataFromLocations와 매우 비슷할까요? – profiler