【问题标题】:mscdex/node-ftp - Await Promise Resolving Too Earlymscdex/node-ftp - 过早地等待 Promise Resolving
【发布时间】:2021-07-04 05:59:19
【问题描述】:

我对异步函数过于陌生,如果我弄错了,很抱歉。目前,我尝试使用 mscdex node-ftp 节点包通过 FTP 上传给定目录中的所有文件夹。虽然这很有效,但承诺在我的函数中解决得太早了,并且会在上传完全完成之前在主函数中运行其他代码。

有没有办法让uploadFolder函数后的代码只有在uploadFolder函数完全完成后才运行?


var c = new Client(config);

let uploadFolder = (folderToUpload) => {
  try {
    return new Promise((resolve, reject) => {
      let folderName = folderToUpload.substring(
        folderToUpload.lastIndexOf("/") + 1
      );

      let replacedItem = folderToUpload.replace(folderName, "");

      let uploadOrder = Glob.sync(folderToUpload + "/**/*");

      c.on("ready", () => {
        let uploadOrderFiltered = uploadOrder.map((element) => {
          return element.replace(replacedItem, "");
        });

        c.mkdir(`/${folderName}`, false, (error) => {
          if (error) {
            console.log(`Error, MKDIR Error ${error}`);
            c.end();
          }
        });

        for (let i = 0; i < uploadOrderFiltered.length; i++) {
          if (uploadOrderFiltered[i].includes(".")) {
            c.put(
              `${replacedItem}${uploadOrderFiltered[i]}`,
              `/${uploadOrderFiltered[i]}`,
              (error) => {
                if (error) {
                  console.log(`Error, UPLOADING Error ${error}`);
                  c.end();
                }
              }
            );
          } else {
            c.mkdir(`/${uploadOrderFiltered[i]}`, false, (error) => {
              if (error) {
                console.log(`Error, MKDIR Error ${error}`);
                c.end();
              }
            });
          }
        }

        c.end();
      });

      c.connect(config);
      resolve();
    });
  } catch (error) {
    console.log(`[FTP ERROR]: ${error}`);
    reject();
  }
};
const { createFolder } = require("./createFolder");

let asyncFunc = async () => {

// ... CODE BEFORE (Runs Fine)

//PROMISE BROKEN
  await uploadFolder(
    "D:/GitHub/Procedural-Artwork-Generation/nexRender/Project/Output/zeroTwoTemplate_AJDKAJKDJKADJKA"
  );
  console.log("[FTP]: Upload Done");

//Upload Done is running before uploadFolder function is completed.

提前感谢您的帮助。

【问题讨论】:

  • 您早在任何异步操作完成之前就解决了您的promise。只有当您收到一个表明所有异步工作都已完成的事件时,您才需要调用 resolve()。另外,你似乎有一个异步循环,所以在所有这些都完成之前你不能调用resolve()。如果你承诺一个在循环中使用的.put() 操作,那么这个问题会更容易解决,然后你可以使用承诺来管理控制流、完成和错误传播。
  • 为这个建议干杯,在进一步查看之后,找到方法来确定哪些函数表明所有异步命令都已完成我找到了 c.on("end") 事件,我已经链接这个有承诺的。
  • 这是大体思路,但您似乎有一个异步操作循环,因此您需要知道循环中的每个操作何时完成。

标签: javascript node.js asynchronous ftp


【解决方案1】:

解决方案正如 jfriend00 所说,我过早地兑现了我的承诺。我最终所做的是将 resolve() 方法替换为 c.on("ready") 块之外的单独事件处理程序,该处理程序侦听“end”事件。

更新后的代码如下


var c = new Client(config);

let uploadFolder = (folderToUpload) => {
  try {
    return new Promise((resolve, reject) => {
      let folderName = folderToUpload.substring(
        folderToUpload.lastIndexOf("/") + 1
      );

      let replacedItem = folderToUpload.replace(folderName, "");

      let uploadOrder = Glob.sync(folderToUpload + "/**/*");

      c.on("ready", () => {
        let uploadOrderFiltered = uploadOrder.map((element) => {
          return element.replace(replacedItem, "");
        });

        c.mkdir(`/${folderName}`, false, (error) => {
          if (error) {
            console.log(`Error, MKDIR Error ${error}`);
            c.end();
          }
        });

        for (let i = 0; i < uploadOrderFiltered.length; i++) {
          if (uploadOrderFiltered[i].includes(".")) {
            c.put(
              `${replacedItem}${uploadOrderFiltered[i]}`,
              `/${uploadOrderFiltered[i]}`,
              (error) => {
                if (error) {
                  console.log(`Error, UPLOADING Error ${error}`);
                  c.end();
                }
              }
            );
          } else {
            c.mkdir(`/${uploadOrderFiltered[i]}`, false, (error) => {
              if (error) {
                console.log(`Error, MKDIR Error ${error}`);
                c.end();
              }
            });
          }
        }

        c.end();
      });

      c.on("end", () => {
        resolve();
      });

      c.connect(config);
    });
  } catch (error) {
    console.log(`[FTP ERROR]: ${error}`);
    reject();
  }
};

【讨论】:

  • 这实际上是如何等待所有上传完成的?
【解决方案2】:

我并没有完全按照您要完成的所有操作,但我认为如果您使用 promise-ftp 库,该库将承诺包装在 node-ftp 库周围,然后您可以使用 @ 987654322@ 用于对所有异步操作进行流控制。

这是关于它的外观的一般想法(我试图在没有真正理解你在做什么并且没有能力测试它的情况下复制你的逻辑 - 所以把它当作方向性的,而不是直接跑出来的东西框):

const Client = require('promise-ftp');
const glob = require('glob-promise');

async function uploadFolder(folderToUpload) {
    let folderName = folderToUpload.substring(folderToUpload.lastIndexOf("/") + 1);
    let replacedItem = folderToUpload.replace(folderName, "");
    let uploadOrder = await glob(folderToUpload + "/**/*");
    const c = new Client(config);
    try {
        await c.connect();
        await c.mkdir(`/${folderName}`, false);
        let uploadOrderFiltered = uploadOrder.map((element) => {
            return element.replace(replacedItem, "");
        });
        for (let f of uploadOrderFiltered) {
            if (f.includes(".")) {
                await c.put(`${replacedItem}${f}`, `/${f}`);
            } else {
                await c.mkdir(`/${f}`, false);
            }
        }
    } catch (e) {
        console.log('FTP error: ', e);
        throw e;
    } finally {
        c.end();
    }
}

关于你的逻辑的一些观察:

  1. 您似乎假设存在“。”在文件名中表示它是一个文件,而不是一个目录。当然不能保证,因为目录可以有“。”在他们中。最好从您的原始文件系统中获取该信息,也许从您的 Glob 调用中获取。
  2. 您正在使用 Glob 的同步版本。 glob-promise 将承诺包装在 glob 接口周围,因此您也可以将 async/await 与它一起使用,然后使用异步版本。
  3. 您的原始代码提前创建了一个客户端对象并将其存储在模块级别,然后重复使用它。如果同时有不止一个对uploadFolder() 的调用,那将产生问题。因此,我为每个单独调用 uploadFolder() 创建了一个新客户端。
  4. 这个新版本通过返回的 Promise 将错误反馈给调用者,因此调用者可以知道它何时完成以及是否有错误。您的代码没有将大部分错误反馈给调用者。

【讨论】:

    猜你喜欢
    • 2020-12-07
    • 2018-04-17
    • 2021-03-22
    • 2020-01-15
    • 1970-01-01
    • 2022-07-20
    • 1970-01-01
    • 2018-02-21
    • 2021-06-30
    相关资源
    最近更新 更多