承诺代码被读取两次

Promise code are read twice

我使用以下代码读取 json 文件和 return 一个承诺 我有两个问题

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 解析完成并且 return 在 json 对象之外(对调用者),当我添加最后一个 then 它不起作用(returned object is undefined), 知道怎么做吗?

UPDATE 我试着按照它不起作用...

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); 来解决它 现在我应该如何解决第一个问题 which method called twice

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

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

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

编辑 在评论中回答你的问题。有几件事你应该记住。

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

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

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');
    });

如果你想处理一次 glob 和一次文件读取,那么你必须更有创意。

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');
    });