簡體   English   中英

Node.js 在遞歸循環中返回一個 promise

[英]Node.js return a promise in recursive loop

我無法讓循​​環以正確的順序返回嵌套的 Promise。 我試圖遞歸地遍歷一個包含子目錄的目錄以獲取文件並將它們復制到一個新目錄。 此功能發生在 Promise 鏈中,因為其他進程需要在復制文件后按順序完成。

下面的代碼有效,但是當我在.then()之后 console.log out 一個字符串時.then()它在承諾解決之前記錄。

代碼是:

    let newDirectory = /// helper function that returns promise of mkdirSync(exampleDir)
    newDirectory.then(function(result){
        getAllFiles('/TestDir').then((result) => {
            console.log(result);
        });
        console.log('this should fire after the "result" from above');
        //// rest of promises in promise chain
    });
        

我正在調用“getAllFiles”的遞歸函數通過“TestDir”及其子文件夾並將文件復制到“ExampleDir”

const getAllFiles = function(dirPath, arrayOfFiles) {
   return new Promise((resolve, reject) => {
   var promises = [];
   files = fs.readdirSync(dirPath)
   arrayOfFiles = arrayOfFiles || []
   files.forEach(function(file) {
     if (fs.statSync(dirPath + "/" + file).isDirectory()) {
        arrayOfFiles =  resolve(getAllFiles(dirPath + "/" + file, arrayOfFiles));
      } else {
      promises.push(helper.copyFile(path.join(dirPath, "/", file),`/TestDir/${file}`))
     }
   }
 })
  Promise.all(promises).then(function(result){
    resolve(result);
    console.log('done with Promises');
  }).catch((error) => {
    reject(error);
  });
}

復制文件的助手在復制文件后返回一個承諾

exports.copyFile = function(file, destDir){
  return new Promise(function ( resolve, reject ){
        fs.copyFile(file, destDir, (err) => {
          if(err) reject( err );
          else resolve('Successfully copied');
        });
   });
}

這實際上似乎有效,但我擔心它不適用於大量數據,因為

console.log('this should fire after the "result" from above');

在其他日志之前觸發。 控制台看起來像:

this should fire after the "result" from above
done with Promises        /// how ever many files are copied
[ 'Successfully copied']   //// with a length of however many files are copied)
done with Promises        //// this is fired once

這是一個預期的日志,還是應該在記錄“'這應該在上面的“結果”之后觸發'”行之前解決並記錄所有承諾?

這是一個預期的日志,還是應該在記錄“'這應該在上面的“結果”之后觸發'”行之前解決並記錄所有承諾?

是的,這是可以預料的。 Promises不適用時,您編寫的代碼就好像它是同步的一樣。 下面的代碼片段中實際發生的事情是在newDirectory Promise 解析之后, getAllFilesconsole.log('this should fire after the "result" from above'); getAllFiles console.log('this should fire after the "result" from above'); 函數將立即執行。 也就是說,console.log 在執行之前不會等待getAllFiles解析。

let newDirectory = /// helper function that returns promise of mkdirSync(exampleDir)
newDirectory.then(function(result){
    getAllFiles('/TestDir').then((result) => {
        console.log(result);
    });
    console.log('this should fire after the "result" from above');
    //// rest of promises in promise chain
});

所以如果你想改變console.log的順序,確保它在getAllFiles Promise解析后執行,你可以重寫如下。

newDirectory.then(function(result){
    getAllFiles('/TestDir').then((result) => {
        console.log(result);
        // or you could add it here
    }).then(() => { 
      console.log('this should fire after the "result" from above');
    });
});

您還應該注意到,我說的是 Promise 何時解析,而不是函數完成執行時。 有一個非常重要的區別。 如果我們再次以上面的示例為例,並說我們想在復制所有文件后執行一些其他操作。

newDirectory.then(function(result){
    getAllFiles('/TestDir').then((result) => {
       ...
    });
}).then(() => {
    console.log('doing some other task');
});

在上面的例子中,會發生什么是newDirectory Promise 將解析,然后getAllFiles將被調用,並且getAllFiles完成執行之前,最終的 console.log 將被記錄。 這是 Promises 的一個重要原則,如果你希望它們同步運行,你需要鏈接 Promises,即你需要通過所有鏈接的 then 函數返回承諾。 因此,要解決上述問題,我們需要返回從getAllFiles函數解析的承諾,如下所示

newDirectory.then(function(result){
    return getAllFiles('/TestDir').then((result) => {
       ...
    });
}).then(() => {
    console.log('doing some other task');
});

fs/promises 和 fs.Dirent

這是一個使用 Node 的快速fs.Dirent對象和fs/promises模塊的高效、非阻塞ls程序。 這種方法允許您跳過每條路徑上浪費的fs.existfs.stat調用。

使用asyncawait ,我們可以避免過多考慮如何專門連接 Promises -

// main.js
import { readdir } from "fs/promises"
import { join } from "path"

async function* ls (path = ".")
{ yield path
  for (const dirent of await readdir(path, { withFileTypes: true }))
    if (dirent.isDirectory())
      yield* ls(join(path, dirent.name))
    else
      yield join(path, dirent.name)
}

async function* empty () {}

async function toArray (iter = empty())
{ let r = []
  for await (const x of iter)
    r.push(x)
  return r
}

toArray(ls(".")).then(console.log, console.error)

讓我們獲取一些示例文件,以便我們可以看到ls工作 -

$ yarn add immutable     # (just some example package)
$ node main.js
[
  '.',
  'main.js',
  'node_modules',
  'node_modules/.yarn-integrity',
  'node_modules/immutable',
  'node_modules/immutable/LICENSE',
  'node_modules/immutable/README.md',
  'node_modules/immutable/contrib',
  'node_modules/immutable/contrib/cursor',
  'node_modules/immutable/contrib/cursor/README.md',
  'node_modules/immutable/contrib/cursor/__tests__',
  'node_modules/immutable/contrib/cursor/__tests__/Cursor.ts.skip',
  'node_modules/immutable/contrib/cursor/index.d.ts',
  'node_modules/immutable/contrib/cursor/index.js',
  'node_modules/immutable/dist',
  'node_modules/immutable/dist/immutable-nonambient.d.ts',
  'node_modules/immutable/dist/immutable.d.ts',
  'node_modules/immutable/dist/immutable.es.js',
  'node_modules/immutable/dist/immutable.js',
  'node_modules/immutable/dist/immutable.js.flow',
  'node_modules/immutable/dist/immutable.min.js',
  'node_modules/immutable/package.json',
  'package.json',
  'yarn.lock'
]

有關遞歸列出目錄的dir程序、用於查找文件的search程序等,請參閱此相關問答

如果你想在Promise之后控制台日志,你必須在.then()

Promise.all(promises).then(function(result) {
  resolve(result);
  console.log('done with Promises');
})
  .then(() => console.log("this should fire after the "result" from above"))
  .catch((error) => reject(error));

這是因為Promise是非阻塞的,它之后的任何東西都不會等待它完成后再執行。

暫無
暫無

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

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