Using Array.map in combination with try/catch can result in delayed reference assignment

After observing a delay in referencing after using [].map with try/catch block inside, I am curious to know the reason behind this behavior and how to prevent it.

My test scenario was as follows:

-file_picked is responsible for handling the change event of an input element with type=file

    file_picked: function(e){

        var flist = e.target.files, //all picked files
            parsed = [],        //all read successfully
            errors = [];        //all errored

        //parsing files
        _.map(flist, function(file){
            var reader = new FileReader();
            //setting up callbacks
            reader.onload = function(e){    //@reading done
                try{
                    var file_cont = e.target.result,
                        parser = new(less.Parser)({
                            filename: file.name
                        });
                    //running the file through the less parser
                    parser.parse(file_cont, function (err, tree) {  //parser done
                        var o = {};
                        if (err) {  //@some parser error occured
                            err('less parser error',err);
                            o[file.name] =  err;
                            errors.push(o);
                        }else{  //@parsed successfully by the less parser
                            o[file.name] =  tree.toCSS();
                            parsed.push(o);
                        }
                    });
                }catch(e){
                    err('reader onload exception',arguments);
                    var o = {}; o[file.name] =  i18n('Parsing failed');
                    errors.push(o);
                }
            };
            reader.onerror = function(e){   //@reading error
                err('reader onerror',arguments);
                var o = {}; o[file.name] =  i18n('Reading failed with error: ')+e.target.error.code;
                errors.push(o);
            };
            //start reading
            reader.readAsText( file );
        });

        //reading completed
            console.log(parsed) 
            for( var i = 0; i < parsed.length; i++ ) {
            console.log(parsed[i],errors);
        }           
    }

The parsed array is visible in the console! However, it never goes through iteration, why?

Thank you in advance

PS.:_.map refers to underscore.js method, err, lg are just wrappers for console.xxx.

Answer №1

If you want the function to iterate through parsed files only after all asynchronous calls have finished, you can achieve it in the following way:

Utilize _.wrap to run a function once it's been called n times, where n is equal to flist.length in your specific scenario

[file_picked: function(e){

    var flist = e.target.files, //all selected files
        parsed = [],        //all successfully read files
        errors = [\],
        readParsed;        //all files with errors

    //parse files
    _.map(flist, function(file){
        var reader = new FileReader();
      //set callbacks
        reader.onload = function(e){    //@reading done
            try{
                var file_cont = e.target.result,
                    parser = new(less.Parser)({
                        filename: file.name
                    });
                //run file through the less parser
                parser.parse(file_cont, function (err, tree) {  //parser done
                    var o = {};
                    if (err) {  //@some parser error occured
                        err('less parser error',err);
                        [file.name] =  err;
                        errors.push(o);
                    }else{  //@parsed successfully by the less parser
                        [file.name] =  tree.toCSS();
                        parsed.push(o);
                    }

                    readParsed();
              });
            }catch(e){
                err('reader onload exception',arguments);
                var o = {}; [file.name] =  i18n('Parsing failed');
                errors.push(o);
                readParsed();
            }
        };

        reader.onerror = function(e){   //@reading error
            err('reader onerror',arguments);
            var o = {}; o\[file.name\] =  i18n('Reading failed with error: ')+e.target.error.code;
            errors.push(o);

            readParsed();
        };
        //start reading
        reader.readAsText( file );
    });

    readParsed = _.after(flist.length, function(){
        console.log(parsed);

        for( var i = 0; i < parsed.length; i++ ) {
            console.log(parsed[i],errors);
        }
    });
}][1]

Answer №2

Although the map function is synchronous, the callback function passed to it performs asynchronous operations.

The operation reader.readAsText( file ); is asynchronous in nature.

As a result, the map function ends before any data gets added to the parsed array. One solution is to track the number of processed files by using counters in the onload and onerror callbacks. Once the number of processed files equals flist.length, you can invoke a function that will work with the parsed data.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Express 4 : In order to pass a variable from app.js to my routes, I have it configured and ready to be sent over

Recently, I made some updates to my app.js file using Express generator. Below is a snippet of the changes: app.js var express = require('express'); var socket_io = require( "socket.io" ); var app = express(); // Socket.io var io = socket_io( ...

The issue with the static Vue component is not resolving as expected. I am seeking a solution

Following the setup of the project using vue-cli 3.x, I declared this component in the main.js file. By the way, Unknown custom element: - have you properly registered the component? For recursive components, ensure to include the "name" option. I am st ...

Obtaining the date without the time in TypeScript and MongoDB

I'm working with TypeScript and have the following code snippet: const EmployeeDetailsSchema: mongoose.Schema = new mongoose.Schema({ employeeId: { type: String }, advance: { lastAdvanceClosedOn: { type: String }, pending: { type: String ...

Troubleshooting the issue with the sequelize hasOne association in Node.js

In my Node.js application utilizing Sequelize to access the database, I have tables named Products, Users, Carts, and CartItems. There are relationships between these tables: Product.belongsTo(User, { constraints: true, onDelete: 'CASCADE' }); ...

403 Error: CSRF token is invalid - Node.js Express with csurf

I've exhausted all resources available on this topic, both here and through Google searches, but I'm still unable to resolve this issue. I am using Node, Express, EJS, and attempting to implement csurf for a form submission via jQuery ajax. No ma ...

Breaking down and modifying JavaScript JSON objects

Can someone explain how to separate a JSON object and make updates based on the ID? I've heard about using stringify! But how do I actually implement the function to update the object? <input type="text" value="{"id":"1","price":"30.00","edit":0}, ...

Arrange the array according to the values within each sub-array

Attempting to organize the items within an object/array based on their "name" attribute, Found guidance on this topic at Sort array of objects and put together the following code sample: var alphabet = { a: 1, b: 2, c: 3, d: 4, e: 5, ...

Extract the data that was returned from the AJAX post function

I am looking to create a condition that is dependent on the data received from an ajax post outside of the post function function post(){ $.post('page.php',$('#form').serialize(), function(data) { if(data !== 'good'){a ...

Having a <cfinput> tag nested inside a JavaScript block is leading to errors

I'm currently working with jQuery to append a new row to a table, but I've encountered an issue when trying to insert a <cfinput> field. Strangely, ColdFusion seems to be interpreting the <cfinput> tag within the JavaScript block and ...

Updating Multiple Characters Simultaneously in Text Using JavaScript

I'm faced with the challenge of coloring a table based on its values. For instance, if a cell contains 150%, it should be red; if it's 50%, it should be green. Unfortunately, the text in my table has spaces and '%' symbols scattered thr ...

Is it possible to operate two controllers with two different routing paths simultaneously?

I am working with two modules that have two different routes: angular .module('lang', ['ngRoute']) .config('lang', ['$routeProvider', config]) .controller('lang', lang); function config(route){ ro ...

Showing a div with 2 unique data attributes - such as displaying Currency and Quantity

My E-Commerce website is created using HTML, JavaScript, and PHP. On the product details page, users can add products to their cart, so I show the total cart value. I need the total amount displayed in a decimal number format (10,2). Currently, when a u ...

Adding a version number to the splash screen in Cordova: A step-by-step guide

After successfully integrating the Cordova Splashscreen plugin into my Ionic project, everything is running smoothly. However, I am now looking to dynamically add a version number to the splash screen without manually editing the Splash Screen PNG file e ...

Can you explain the purpose of prevState within the setState method of a functional component?

When returning the updated previous state within a setState method retrieved from the useState hook, it appears that the state remains unchanged. To demonstrate this behavior, consider running the following code snippet: function App(){ const [state, ...

Iterate through array starting from the center and moving outwards

I am looking to iterate through an array starting from the middle and moving outwards. var array = [a,b,c,d,e]; The desired order of printing would be: c,d,b,e,a I have managed to divide the array in half and traverse it both forward and backward, but I ...

Attempting to grasp the sequence in which setTimeout is ordered alongside Promise awaits

I've been puzzling over the sequence of events in this code. Initially, I thought that after a click event triggered and Promise 2 was awaited, the for loop would resume execution once Promise 1 had been resolved - however, it turns out the outcome is ...

Showing attributes of models using Sequelize and Handlebars

As a novice in the world of programming, I am currently immersed in a website project where users can write and post articles. One crucial aspect I am focusing on is displaying the history of articles written by each user on their account page. Despite uti ...

A platform for creating ER and flow diagrams specifically tailored for web applications, utilizing open source software

Our team is currently working on creating a web application that enables users to create diagrams, such as flow or ER diagrams. We are looking for ways to convert these diagrams into XML or other formats for representation. Are there any open-source soft ...

Incorporating a custom object into two dropdown menus using JavaScript

As a novice in the world of JavaScript, I am attempting to dynamically add values from a text box to two select boxes. Upon entering a value (node name), my goal is to include that name in both select boxes srcNodes and targetNodes. However, I'm enco ...

Troubleshoot React component re-rendering issue

I'm currently facing a challenging bug that only occurs very sporadically (about once every few dozen attempts). During the render call, I'm determined to gather as much information as possible: I want to understand what triggered the rerender ...