【问题标题】:Promise code are read twice承诺代码被读取两次
【发布时间】:2015-08-05 03:09:39
【问题描述】:

我使用以下代码读取 json 文件并返回一个 promise 我有两个问题

return globAsync("folder/*.json").catch(function (err) {
    throw new Error("Error read: " + err);
}).map(function (file) {
    return fs.readFileAsync(file, 'utf8')
        .then(function (res) {
            console.log("test");
            return JSON.parse(res); 
        },
        function (err) {
            throw new Error("Error :" + err);
        }).then(function () {
            console.log("test2");
        });
});

我使用控制台日志,我看到控制台打印了两次

test
test
test2
test2

为什么会发生以及如何避免?

  1. 在我放置console.log("test2"); 的地方我需要调用事件 json 解析完成并且仍然返回到 json 对象之外(给调用者),当我添加最后一个 then 它不起作用(返回的对象未定义),知道该怎么做吗?

更新我尝试跟随它不起作用...

return globAsync("folder/*.json").catch(function (err) {
            throw new Error("Error read: " + err);
        }).map(function (file) {
            return fs.readFileAsync(file, 'utf8')
                .then(function (res) {
                    console.log("test");
                    JSON.parse(res); //data parse
                }.catch(function (err) {
                        throw new Error("Error :" + err);
                    }
                ).then(function (data) {
                        obj.emit('ready');
                        return data;
                    }))
        });
    }

UPDATE2 我可以通过简单地添加新的return JSON.parse(res); 来解决它 现在我应该如何解决方法调用两次

的第一个问题

【问题讨论】:

  • 我猜它被调用了两次,因为 globAsync 返回一个长度为 2 的数组。我不明白你的第二个问题
  • @JaromandaX - 有一种方法可以通过 promises 功能避免这种情况,因为它是多余的......第二个问题是假设我需要在 parse.json 完成时调用某个事件,在哪里以及如何这样做?
  • @JaromandaX - 另见我更新的问题,也许现在问题 2 更清楚......
  • 只是说,obj.emit('ready'); 几乎没有意义。只需将承诺用作承诺,否则您将依赖 obj 事件发射器。
  • 只是问,你和@shopiaT一起工作吗?您的代码与that one 非常相似 :-)

标签: javascript promise bluebird


【解决方案1】:

就像@jaromandaX 所说,您可能有两个*.json 文件。尝试打印出文件名,它应该会变得更加明显。在这种情况下,.map 预计会被调用两次,每个文件调用一次。否则你将无法同时读取和解析两个文件。

如果你想让它在所有文件读取和解析完成后收敛到一个点,那么你需要在.map 之后链接另一个.then。例如。

return globAsync("folder/*.json")
    .map(function(file) {
        ...
    })
    .then(function() {
        obj.emit('ready');
    });

编辑在评论中回答您的问题。您应该记住一些事项。

  1. 在 Promise 链中抛出 Error 将被 Promise 捕获并将其发送到拒绝流中。如果您对获取自定义错误类型或以理想方式打印堆栈跟踪感兴趣,您仍然可能会抛出错误。但大多数人更喜欢return Promise.reject(error)
  2. .map 中的任何拒绝都会将承诺链发送到拒绝流中。
  3. 在拒绝链中,如果您想继续拒绝流程。您需要return Promise.reject(error),否则如果您不返回拒绝对象,您可以将其带回解析流程。

如果你想单独处理每个错误,你可以这样做:

return globAsync("folder/*.json")
    .catch(function(error) {
        // TODO: Handle error
        return Promise.reject(error);
    })
    .map(function(file) {
        return fs.readFileAsync(file, 'utf8')
            .catch(function(error) {
                // TODO: Handle error
                return Promise.reject(error);
            })
            .then(function(res) {
                return JSON.parse(res);
            });
    })
    .then(function() {
        obj.emit('ready');
    });

如果你想处理一次全局和一次文件读取,那么你必须获得更多的创意。

return globAsync("folder/*.json")
    .catch(function(error) {
        // TODO: Handle error
        return Promise.reject(error);
    })
    .then(function(files) {
        return Promise.resolve(files)
            .map(function(file) {
                return fs.readFileAsync(file, 'utf8');
            })
            .catch(function(error) {
                // TODO: Handle error once for any read error
                return Promise.reject(error);
            })
            .map(function(res) {
                // Judging by your original code, you are not handling
                // parser error, so I wrote this code to behave equivalent
                // to your original. Otherwise chain parse immediate after
                // readFileAsync.
                return JSON.parse(res);
            });
    })
    .then(function() {
        obj.emit('ready');
    });

【讨论】:

  • 谢谢 1+ 假设您需要处理错误 1.来自 glob 的错误 2.来自读取文件 asyc 的错误,根据我的帖子代码流程,您将如何做到这一点。谢谢前进!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-29
  • 2016-01-08
  • 2017-01-09
  • 2018-10-06
  • 1970-01-01
相关资源
最近更新 更多