3

I use the following code to read json file and return a promise I've two questions

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

I use the console log and I see that the console is printed twice

test
test
test2
test2

why its happening and how to avoid it ?

  1. In the place I've put console.log("test2"); I need to invoke event that the json parse is finished and still return outside the json object (to the caller), when I add the last then it doesn't work(the returned object is undefined),any idea how to do that right?

UPDATE I try like following which it doesn't work...

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 I was able to solve it by simply add new return JSON.parse(res); Now how should I solve the first issue which method called twice

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • 1
    my guess is it's called twice because globAsync is returning an array of length two. I don't understand your second question – Jaromanda X Aug 03 '15 at 06:59
  • @JaromandaX - There is a way to avoid this with promises functionality since its redundant...the second question is assume that I need to invoke some event when the parse.json was finished,where and how to do that ? –  Aug 03 '15 at 07:03
  • @JaromandaX - see also my updated question, maybe now question 2 is more clear... –  Aug 03 '15 at 07:10
  • Just saying, `obj.emit('ready');` hardly makes sense. Just use the promise as a promise where otherwise you'd rely on that `obj` event emitter. – Bergi Aug 04 '15 at 19:09
  • Just asking, are you working together with [@shopiaT](http://stackoverflow.com/users/3012133/shopia-t)? Your code is very similar to [that one](http://stackoverflow.com/a/31773139/1048572) :-) – Bergi Aug 04 '15 at 19:15

1 Answers1

0

Like @jaromandaX said, you probably got two *.json files. Try to print out the file name instead and it should become more obvious. In that case, .map is expected to be called twice, once for each file. Otherwise you aren't gonna be able to read and parse two files together.

If you want to get it to converge to a single point after all file reads and parses are complete, then you need to chain another .then after .map. eg.

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

EDIT To answer your question in comment. There are a few things you should keep in mind.

  1. Throwing Error inside the promise chain will get caught by the promise and send it into the rejection flow. You may still throw an error if you are interested in getting custom error type or printing stack trace in a desirable way. But most people prefer return Promise.reject(error).
  2. Any rejection in .map will send the promise chain into rejection flow.
  3. Inside the rejection chain, if you want to continue down the rejection flow. You need to return Promise.reject(error), otherwise if you don't return a reject object, you can bring it back into resolve flow.

If you want to want to handle each error individually, you can do something like this:

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

If you want to handle once for glob and once for file read, then you have to get a bit more creative.

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');
    });
initialxy
  • 1,193
  • 8
  • 17
  • Thanks 1+ assume that you need to handle errors 1.errors that coming from glob 2. errors that are coming from read file asyc ,how would you do that according to my post code flow.Thanks in advance! –  Aug 03 '15 at 09:53