【问题标题】:Javascript recursive function with promises带有承诺的 Javascript 递归函数
【发布时间】:2015-12-05 08:08:01
【问题描述】:

以下函数用于通过对嵌入式网络服务器的 HTTP 调用删除文件。网络服务器接受 DELETE 动词来删除文件;它也适用于文件夹,但前提是为空。

我想创建一个递归请求文件夹内容的函数,将其全部删除,然后删除该文件夹。 使用 Promise,我设法让它几乎完全工作。 顶部路径“/”中的文件被删除,第一个文件夹“/folder1/”中的文件也被删除,但随后函数退出,“/folder1/”为空,“/folder2/”保持不变。 在 Firefox 40 和 Chrome 45 中都试过了。 有人能发现任何错误吗?我已经尝试了几天...

我认为这可能与 jQuery 承诺和标准承诺的混合使用有关,没有正确解决。

文件夹内容是通过一个 API 调用来询问的,该调用返回一组对象,如下所示:

[{"type": "dir", "name": ".", "size": "-"},
{"type": "dir", "name": "..", "size": "-"},
{"type": "dir", "name": "folder1", "size": "-"},
{"type": "dir", "name": "folder2", "size": "-"}
{"type": "file", "name": "file1", "size": "1211"},
{"type": "file", "name": "file2", "size": "1251"},]

这里是主要功能。

function purgeSdcard() {
    printMessage("WARNING: Purging SDcard");

    function purgeCycle(fileList, path, file) {
        return new Promise(function(resolve, reject) {
            if (typeof(fileList) === 'undefined' || fileList.length === 0) resolve();
            if (typeof(file) === 'undefined') file = 0;
            if (file >= fileList.length) resolve();

            if (fileList[file].type === "dir") { // Directory
                if (fileList[file].name === "." || fileList[file].name === ".." || fileList[file].name === "system volume information") {
                    return purgeCycle(fileList, path, file + 1);
                }
                else {
                    return purge(path + fileList[file].name + "/")
                        .then(function(res) {
                            return deleteFile(path + fileList[file].name);
                        })
                        .then(function(res) {
                            console.log("purgeCycle(fileList, path, file + 1) " + fileList + path + file + 1);
                            return purgeCycle(fileList, path, file + 1);
                        });
                }
            }
            else { // File
                return deleteFile(path + fileList[file].name).then(
                    function(res) {
                        return purgeCycle(fileList, path, file + 1);
                    });
            }
        });
    }

    function purge(path) {
        if (typeof(path) === 'undefined') path = "/";
        return getFileList(path).then(
            function(fileList) {
                return purgeCycle(fileList, path);
            });
    }

    purge();
}

function getFileList(path, onDone) {
    if (typeof(path) === 'undefined') path = "/";
    return Promise.resolve($.ajaxQueue({
            url: path + "?dir",
            type: "GET",
            dataType: "json"
        }).done(function() {
            console.log("getFileList: " + path);
            if (typeof(onDone) === 'function') {
                onDone();
            }
        })
        .fail(function() {}));
}

function deleteFile(fileURL) {
    printMessage("ST &nbsp- File delete: " + fileURL);
    return Promise.resolve($.ajaxQueue({
            url: fileURL,
            type: "DELETE"
        }).done(function() {
            printMessage("OK &nbsp- File delete: " + fileURL);
        })
        .fail(function() {
            printError("ERR - File delete: " + fileURL);
        }));
}

【问题讨论】:

  • 欢迎登上 Riccardo!顺便说一句,我建议你的应用不要完全模仿底层文件系统的行为:如果你想删除一个文件夹,让服务器处理所有逻辑,而不需要运行大量的 DELETE 请求。
  • 谢谢@moonwave99!好吧,确实可以编写一个自定义 API 来一次性删除所有文件,这就是 B 计划。但我想知道这里有什么问题,因为我在这上面花了很多时间。
  • 我实际上不知道,但考虑导航所有树,列出所有内容,然后删除所有文件,然后删除所有目录(然后将是空的)。然后递归将只获取内容列表。
  • @moonwave99 我按照你的建议做了并实施了“Plan B”(自定义“api/purgesdcard”),现在应用程序正在运行。

标签: javascript jquery ajax recursion promise


【解决方案1】:

我认为你的猜测是正确的,这可能是 jQuery Promises/Deferred 对象和 JS 内置 Promises 之间的冲突。

Promise.resolve() 返回一个promise,它解析为传递给函数的值,或者如果传递了promise,则返回promise 解析为的值。由于您向它传递了一个 jQuery.Deferred,它会解决这个问题,所以任何时候您调用 getFileList(...).then(fileList),您收到的 fileList 实际上就是 Deferred 对象。

在这种情况下,您既可以在代码中使用 jQuery.Deferred 对象,也可以将 $.ajaxQueue 包装在 Promise 中以使其兼容。您使用new PromisePromise.resolve 的事实让我相信您并没有对jQuery.Deferred 投入太多,所以这里有一个使用$.ajaxQueue 的包装版本的相关代码示例:

function ajaxQueue(options) {
    return new Promise(function (resolve, reject) {
        $.ajaxQueue(options).done(function (val) {
            resolve(val);
        }).fail(function (err) {
            reject(err);
        });
    });
}

// returns Promise that resolves to a jQuery deferred value.
function getFileList(path, onDone) {
    if (typeof path === 'undefined') path = "/";
    return ajaxQueue({
        url: path + "?dir",
        type: "GET",
        dataType: "json"
    }).then(function (result) {
        console.log("getFileList: " + path);
        if (typeof onDone === 'function') {
            onDone();
        }
        // Pass along.
        return result;
    }).catch(function (err) { });
}

function deleteFile(fileURL) {
    printMessage("ST &nbsp- File delete: " + fileURL);
    return ajaxQueue({
        url: fileURL,
        type: "DELETE"
    }).then(function (result) {
        printMessage("OK &nbsp- File delete: " + fileURL);
        // Pass along result.
        return result;
    }).catch(function (err) {
        printError("ERR - File delete: " + fileURL);
    });
}

常规 JS PromisejQuery.Deferred(除了可用的函数)之间的主要区别在于 then 的返回值是传递给后续 then 调用的值。因此,如果您只是在 getFileListdeleteFile 中记录某些内容,则传递结果很重要。

【讨论】:

  • 谢谢@Chris,我尝试了你的方法,但功能和以前完全一样:顶部路径“/”中的文件被删除,第一个文件夹“/folder1/”中的文件也被删除,但随后函数退出,“/folder1/”为空,“/folder2/”保持不变。关于 Promises 兼容性问题,我认为我只需在每个函数中添加 Promise.resolve($.ajax(...)) 就可以正确处理它,正如 here 所建议的那样。也许问题出在实现递归的方式上。
猜你喜欢
  • 1970-01-01
  • 2017-12-18
  • 2017-05-09
  • 2018-12-01
  • 2018-10-11
  • 2015-05-15
  • 1970-01-01
  • 2017-04-08
  • 2019-05-25
相关资源
最近更新 更多