简体   繁体   中英

How to assign a variable in callback function in a callback function in javascript

So I have found this question which seems pretty similar but I do not understand the answer at all I tried to implement it but I do not recognize the patterns of the answer in my code. similar question

Now here is my problem, I have this piece of code :

var fs = require('fs');
var index = JSON.parse(fs.readFileSync('../data/7XXX7/index.json', 'utf8'));
window = {};
var indicators = require('./indicators');
var parser = new window.patient.Indicator('tes', 'test');
var i = 0;

function create_indicators() {
    var result = [];
    fs.readdirSync('../data/7XXX7/files/').forEach(file => {
        fs.readFile('../data/7XXX7/files/' + file, 'utf8', function (err, data) {
            if (err)
                throw err;
            let $ = {};
            $.poids = parser.poids(data);
            $.taille = parser.taille(data);
            $.temperature = parser.temperature(data);
            $.tension = parser.tension(data);
            $.pouls = parser.pouls(data);
            $.ps = parser.ps(data);
            $.saturation = parser.saturation(data);
            for (var j in index.files)
            {
                if (index.files[j].name === file)
                {
                    $.id = index.files[j].name;
                    $.date = index.files[j].date;
                    $.name = index.files[j].IntituleSession;
                    break;
                }
            }
            if ($.poids || $.taille || $.temperature || $.tension || $.pouls || $.ps || $.saturation)
            {
                result.push($);
                console.log(result); // print the actual state of result
//              console.log(i); prints 0 then 1 then ...
                i++;
            }

        });
        console.log(i); // prints 0
    });
    console.log(result); // prints []
    return result;
}

let result = create_indicators();
 console.log(result); // prints []

And it displays :

[]

Why does the callback function in readFile has it's own variables ? Cause it's asynchronous ? But when I use readFileSync it doesn't work too.

How to make result get all the values I put into it ? when I console log result after result.push($); it works so that's not my parser, i is also properly indented each time.

Here's your problem:

   fs.readFileSync('../data/7XXX7/files/' + file, 'utf8', function (err, data) {

The readFileSync doesn't take a callback as an argument. It returns the data or raises an exception. It is synchronous (as the "Sync" in the name suggests) and you're using it as if it was asynchronous.

See the docs:

  1. readFileSync doesn't callback. It is synchronous.
  2. use fs.readdir to get the list of files you want to read. See How do you get a list of the names of all files present in a directory in Node.js?
  3. Need to understand how callback works.

readFileSync doesn't callback. It might be helpful to explain how callback works in asynchronous fs.readFile and fs.readdir

When you are doing asynchronous operations, because you don't know when it is going to be finished, you pass in a function (callback) in the parameter, and run it at the end of the operation.

fs.readFile('/etc/passwd', function (err, data) {
  if (err) throw err;
  console.log(data);
});

fs.readFile in the above code will run the function (err, data) when it finishes executing and pass in the data as the second parameter. If error occurs it will pass in the error as the first parameter.

You can also get a callback function defining what to do when the parsing is over. The callback will need to take error and result. (if you need the error)

Read: http://fredkschott.com/post/2014/03/understanding-error-first-callbacks-in-node-js/

So your create_indicators function should take a callback function.

fs = require("fs")
function create_indicators(folderPath, callback) {
    let result = [];
    fs.readdir(folderPath, (err, files) => {
        if (err)
            callback(err, null); //pass the error to callback if there is any
        else {

            files.forEach((file, index, filesArray) => {
                fs.readFile(file, (err, data) => {
                    if (err)
                        callback(err, null); //pass the error to callback if there is any
                    else {

                        //.....parse....
                        result.push(data);

                        // pass data to callback function when it is the last result
                        if (result.length == filesArray.length)
                            callback(null, result);

                    }
                });
            });

        }
    })
}

When you call it, pass in what you want to do with the result and error as a function.

   create_indicators(".", function(err,result){
    if (err)
      console.error("Got error:", err);
    else
      console.log("Got result:", result);
      //do what you want with the final result
    })

Once you got the callback working, look into Promise which will make this procedure cleaner and easier. Read: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

Your code doesn't wait for the files to get read and have the result pushed to result before moving on. Where you're doing asynchronous operations on items in an array, I would recommend using promises and using Promise.all() to wait for each file to get read and processed before you try using the result. You could do something like this:

function create_indicators() {
    const result = fs.readdirSync('../data/7XXX7/files/').map(file => 
        new Promise((resolve, reject) => {
            fs.readFile('../data/7XXX7/files/' + file, 'utf8', (err, data) => {
                if (err) reject(err);
                // do whatever
                if ($.poids || /* ... */ $.saturation) {
                    // ...
                    resolve($); // instead of `result.push($);`
                } else {
                    resolve(); // can't reject for `Promise.all()` to work
                }
            })
        }));
    return Promise.all(result).then(items => items.filter(item => item));
}

create_indicators().then(indicators => {
    // do something with your list of indicators
}).catch(err => {
    // handle error
});

It creates a promise for each file in your directory that resolves when the file has been processed. It resolves with the item if there is one or nothing if your condition is not met, rejecting if there's an error (promise equivalent to throw ). Since you only want the items that meet your condition, you can then do a filter on the result of Promise.all() to get rid of any undefined in the array (you could also get rid of the condition checking in the fs.readFile callback and do it instead in the filter if you'd like). This returns a promise that resolves with your filtered list.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM