簡體   English   中英

從目錄中在 Node Mongo 驅動程序中構建集合的最佳方法

[英]Best Way to Build a Collection in Node Mongo Driver from a Directory

昨天問了一個與此類似的問題,但解決方案非常簡單,並沒有真正解決我不了解異步 JavaScript 中的流控制的根本問題。 我正在嘗試做的簡短版本是從 JSON 文件的目錄中構建 MongoDB 集合。 我讓它工作了,但我修改了一些東西,現在流程使得程序運行完成,因此在異步insertOne()調用執行之前關閉連接。 最終執行insertOne()調用時,未輸入數據,並且我收到有關使用關閉連接的未處理異常的警告。

我是新手,所以如果我所做的不是最佳實踐(不是),請告訴我,我很樂意改變事情以使其可靠。 相關代碼基本上是這樣的:

fs.readDirSync(dataDir).forEach(async function(file){

    //logic to build data object from JSON file

    console.log('Inserting object ' + obj['ID']);
    let result = await connection.insertOne(obj);
    console.log('Object ' + result.insertedId + ' inserted.');

})

以上內容包含在我awaitasync function 中。 通過在程序流的末尾放置一個console.log()消息,然后是一個while(true); ,我已經驗證了所有“'Inserting object' + obj[ID]”消息都打印出來了,但是當流程到達程序末尾時,下面的“'Object' + result.insertedId + 'inserted'”消息卻沒有打印出來。 如果我刪除while(true); 我收到所有錯誤消息,因為我不再阻塞,顯然到那時客戶端已關閉。 在任何情況下都沒有實際構建數據庫。

我知道總是有學習曲線,但是不能做像流量控制這樣簡單的事情真的很令人沮喪。 我只是想做一些簡單的事情,例如“循環遍歷每個文件,對每個文件執行 function,關閉並退出”,這是補救性編程。 那么,在所有將數據插入到集合中的嘗試都完成之前,標記流控制不會通過的點的最佳方法是什么(成功或不成功,因為理想情況下我可以使用標志來標記是否有任何錯誤)?

我找到了一點答案。 這是一個 hack,但是在網上進一步搜索並且缺乏響應表明可能沒有真正好的方法來可靠地使用異步代碼和回調控制流。 基本上,修改大致如下:

fs.readDirSync(dataDir).forEach(async function(file){

    jobsOutstanding++;

    //logic to build data object from JSON file

    console.log('Inserting object ' + obj['ID']);
    let result = await connection.insertOne(obj);
    console.log('Object ' + result.insertedId + ' inserted.');

    jobsOutstanding--;

})

其中jobsOutstanding是具有訪問器numJobsOutstanding()的模塊的頂級變量。

我現在像這樣包裝關閉(通過一些日志記錄來觀察流程是如何工作的):

async function closeClient(client){
    console.log("Enter closeClient()");
    if(!client || !client.topology || !client.topology.isConnected()){
        console.log("Already closed.");
    }
    else if(dataObject.numJobsOutstanding() == 0){
        await client.close();
        console.log("Closed.");
    }
    else{
        setTimeout(function(){ closeClient(client);}, 100);
    }
}

我讓這個正確運行,並且日志記錄對於可視化異步隊列很有趣。 我不會接受這個答案,看看是否有人知道更好的東西。

我找到了比我原來的更好的答案,所以我將把它發布給未來需要這個的任何人,因為那里似乎沒有太多。 我也會保留我最初的技巧,因為對於任何對異步隊列感興趣的人來說,這是一個有趣的實驗。 我還要為大家指出,有一個非常明顯的方法Promise.allSettled() ,但似乎這會將所有文件一次放入 memory 這是我試圖避免的,所以我不會寫那個解決方案也是。

此方法使用Node fs Promises API ,特別是fsPromises readdir方法。 我將展示運行我在同一目錄中創建的三個測試文件的結果,這些測試文件中到處都是console.log()消息,以幫助理解程序流程。

第一個文件( without-fs-prom.js )使用普通的讀取方法並演示了問題。 如您所見,異步函數( doFile()調用)直到結束才會終止。 這意味着您想要在處理完所有文件之后運行的任何內容都將在處理完成之前運行。

/*
** This version loops through the files and calls an asynchronous
** function with the tradidional fs API (not the Promises API).
*/

const fs = require('fs');

async function doFile(file){
  console.log(`Doing ${file}`);
  return true;
}

async function loopFiles(){

  console.log('Enter loopFiles(), about to loop through the files.');

  fs.readdirSync(__dirname).forEach(async function(file){
    console.log(`About to do file ${file}`);
    ret = await doFile(file);
    console.log(`Did file ${file}, returned ${ret}`);
    return ret;
  });

  console.log('Done looping through the files, returning from loopFiles()');

}

console.log('Calling loopFiles()');
loopFiles();
console.log('Returned from loopFiles()');

/* Result of run:

> require('./without-fs-prom')
Calling loopFiles()
Enter loopFiles(), about to loop through the files.
About to do file with-fs-prom1.js
Doing with-fs-prom1.js
About to do file with-fs-prom2.js
Doing with-fs-prom2.js
About to do file without-fs-prom.js
Doing without-fs-prom.js
Done looping through the files, returning from loopFiles()
Returned from loopFiles()
{}
> Did file with-fs-prom1.js, returned true
Did file with-fs-prom2.js, returned true
Did file without-fs-prom.js, returned true

*/

可以使用 fsPromises API 部分解決該問題,如with-fs-prom1.js如下所示:

/*
** This version loops through the files and calls an asynchronous
** function with the fs/promises API and assures all files are processed
** before termination of the loop.
*/

const fs = require('fs');

async function doFile(file){
  console.log(`Doing ${file}`);
  return true;
}

async function loopFiles(){

  console.log('Enter loopFiles(), read the dir');

  const files = await fs.promises.readdir(__dirname);

  console.log('About to loop through the files.');

  for(const file of files){
    console.log(`About to do file ${file}`);
    ret = await doFile(file);
    console.log(`Did file ${file}, returned ${ret}`);
  }

  console.log('Done looping through the files, returning from loopFiles()');

}

console.log('Calling loopFiles()');
loopFiles();
console.log('Returned from loopFiles()');

/* Result of run:

> require('./with-fs-prom1')
Calling loopFiles()
Enter loopFiles(), read the dir
Returned from loopFiles()
{}
> About to loop through the files.
About to do file with-fs-prom1.js
Doing with-fs-prom1.js
Did file with-fs-prom1.js, returned true
About to do file with-fs-prom2.js
Doing with-fs-prom2.js
Did file with-fs-prom2.js, returned true
About to do file without-fs-prom.js
Doing without-fs-prom.js
Did file without-fs-prom.js, returned true
Done looping through the files, returning from loopFiles()

*/

在這種情況下,異步 function 中的文件迭代循環之后的代碼本身會在處理完所有文件后運行。 您可以在任何function 上下文中使用以下構造的代碼(文件with-fs-prom2.js ):

/*
** This version loops through the files and calls an asynchronous
** function with the fs/promises API and assures all files are processed
** before termination of the loop. It also demonstrates how that can be
** done from another asynchrounous call.
*/

const fs = require('fs');

async function doFile(file){
  console.log(`Doing ${file}`);
  return true;
}

async function loopFiles(){

  console.log('Enter loopFiles(), read the dir');

  const files = await fs.promises.readdir(__dirname);

  console.log('About to loop through the files.');

  for(const file of files){
    console.log(`About to do file ${file}`);
    ret = await doFile(file);
    console.log(`Did file ${file}, returned ${ret}`);
  }

  console.log('Done looping through the files, return from LoopFiles()');

  return;

}

async function run(){
  console.log('Enter run(), calling loopFiles()');
  await loopFiles();
  console.log('Returned from loopFiles(), return from run()');
  return;
}

console.log('Calling run()');
run();
console.log('Returned from run()');

/* Result of run:

> require('./with-fs-prom2')
Calling run()
Enter run(), calling loopFiles()
Enter loopFiles(), read the dir
Returned from run()
{}
> About to loop through the files.
About to do file with-fs-prom1.js
Doing with-fs-prom1.js
Did file with-fs-prom1.js, returned true
About to do file with-fs-prom2.js
Doing with-fs-prom2.js
Did file with-fs-prom2.js, returned true
About to do file without-fs-prom.js
Doing without-fs-prom.js
Did file without-fs-prom.js, returned true
Done looping through the files, return from LoopFiles()
Returned from loopFiles(), return from run()

*/

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM