首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何等待Javascript中的递归承诺

如何等待Javascript中的递归承诺
EN

Stack Overflow用户
提问于 2019-05-23 19:02:21
回答 6查看 2.2K关注 0票数 2

我已经用javascript编写了一个递归承诺,它似乎运行良好,但我希望使用setTimeout()对其进行测试,以确保在继续执行之前我等待的是正确的。下面是我的代码要点:

代码语言:javascript
复制
try{
  await renameFiles(); // <-- await here
  console.log("do other stuff");
}
catch(){
}

const renameFiles = (path) => {
  return new Promise(resolve => {
    console.log("Renaming files...");

    fs.readdirSync(path).forEach(file) => {
      // if file is a directory ...
      let newPath = path.join(path, file);
      resolve( renameFiles(newPath) ); // <- recursion here!
      // else rename file ...
    }
    resolve();
  })

我用这样的setTimeout()测试了它:

代码语言:javascript
复制
const renameFiles = () => {
  return new Promise(resolve => {
    setTimeout(() => {
    // all previous code goes here
    },2000)
  }
}

产出如下:

代码语言:javascript
复制
"Renaming files..."
"Renaming files..."
// bunch of renaming files...
"do other stuff"
"Renaming files..."
"Renaming files..."

所以看起来它还在等待一段时间,但是它会在某个时候继续执行。

我也怀疑我是不是测试错了。知道问题出在哪里了吗?

EN

回答 6

Stack Overflow用户

回答已采纳

发布于 2019-05-23 19:44:42

如前所述,多个解析调用没有意义。然而,这并不是代码中唯一的问题。当对第一个子目录的递归调用启动时,根调用就得到了解决。此代码将按层次顺序处理目录。

rename.js

代码语言:javascript
复制
const fs = require('fs');
const path = require('path');

const inputPath = path.resolve(process.argv[2]);
const newName = 'bar.txt';

async function renameFiles(filePath) {
    for (const file of fs.readdirSync(filePath)) {
        const newPath = path.join(filePath, file);
        const descriptor = fs.lstatSync(newPath);
        if (descriptor.isDirectory()) {
            await renameFiles(newPath)
        } else if (descriptor.isFile()) {
            await renameFile(file);
        }
    }
}

async function renameFile(file) {
    console.log(`Renaming ${file} to ${newName}`)
    return new Promise(resolve => {
       setTimeout(() => {
           console.log(`Renamed ${file} to ${newName}`)
           resolve();
       }, 300)
    });
}

async function main() {
    console.log(`Renaming all files in ${inputPath} to ${newName}`);
    await renameFiles(inputPath);
    console.log('Finished');
}

main();

你可以像

代码语言:javascript
复制
node rename.js relativeFolderName

或者,如果订单不重要,那么您可以使用@Tiago中提到的mapPromise.all

代码语言:javascript
复制
const renameFiles = async path => {
    const renamePromises = fs.readdirSync(path).map(file => {
      if (isDirectory(file)) {
          const newPath = path.join(path, file);
          return renameFiles(newPath)
      } else {
          return renamefile(file);
      }  
    });
    await Promise.all(renamePromises);
}
票数 4
EN

Stack Overflow用户

发布于 2019-05-23 19:41:36

要使此工作正常,您需要等待目录中的所有文件进行解析。因此,您需要执行一个Promise.all,使用map而不是forEach

就像这样:

代码语言:javascript
复制
try{
  await renameFiles(); // <-- await here
  console.log("do other stuff");
}
catch(){
}

const renameFiles = (path) => {
  return new Promise(resolve => {
    console.log("Renaming files...");

    const allFilesRenamePromises = fs.readdirSync(path).map(file => {
      if(file.isDirectory()) {
        let newPath = path.join(path, file);
        return renameFiles(newPath); // <- recursion here!
      } else {
        // rename file ...
      }
    }
    resolve(Promise.all(allFilesRenamePromises));
  })
票数 0
EN

Stack Overflow用户

发布于 2019-05-25 03:55:23

我建议采用一种更分解的方法,而不是编写一个大的复杂函数。

首先,我们从一个files开始,它递归地列出指定path中的所有文件-

代码语言:javascript
复制
const { readdir, stat } =
  require ("fs") .promises

const { join } =
  require ("path")

const files = async (path = ".") =>
  (await stat (path)) .isDirectory ()
    ? Promise
        .all
          ( (await readdir (path))
              .map (f => files (join (path, f)))
          )
        .then
          ( results =>
             [] .concat (...results)
          )
    : [ path ]

我们现在有一种方法来列出所有的文件,但是我们只想重命名其中的一些文件。我们将编写一个通用的search函数来查找与查询匹配的所有文件-

代码语言:javascript
复制
const { basename } =
  require ("path")

const search = async (query, path = ".") =>
  (await files (path))
    .filter (x => basename (x) === query)

现在我们可以把你的renameFiles函数写成search的专门性-

代码语言:javascript
复制
const { rename } =
  require ("fs") .promises

const { dirname } =
  require ("path")

const renameFiles = async (from = "", to = "", path = ".") =>
  Promise
    .all
      ( (await search (from, path))
          .map
            ( f =>
                rename
                  ( f
                  , join (dirname (f), to)
                  )
             )
       )

要使用它,我们只需使用它的预期参数来调用renameFiles -

代码语言:javascript
复制
renameFiles ("foo", "bar", "path/to/someFolder")
  .then
    ( res => console .log ("%d files renamed", res.length)
    , console.error
    )

// 6 files renamed

回顾上面的程序,我们看到随着Promise.allawaitmap的使用,出现了一些模式。事实上,这些模式可以被提取,我们的程序可以进一步简化。下面是filesrenameFiles修改后使用的通用Parallel模块-

代码语言:javascript
复制
const files = async (path = ".") =>
  (await stat (path)) .isDirectory ()
    ? Parallel (readdir (path))
        .flatMap (f => files (join (path, f)))
    : [ path ]

const renameFiles = (from = "", to = "", path = "") =>
  Parallel (search (from, path))
    .map
      ( f =>
          rename
            ( f
            , join (dirname (f), to)
            )
      )

Parallel模块最初是在this related Q&A中派生的。要获得更多的洞察力和解释,请点击链接。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/56281473

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档