javascript - program fails because readFile is asynchronous? -


i'm trying combine 2 lists. list1 has 681 french verbs, list2 has 681 translations. i'm using javascript, , node.js read files. here's attempt:

    var frenchwords, englishwords, combinedlist;     fs = require('fs')  // 1. read french file      fs.readfile('frenchverbslist.txt', 'utf8', function (err,data) {     if (err) {         return console.log("error here!: " + err);     }     frenchwords = data.split('\n');     });  //read english file      fs.readfile('englishverbslist.txt', 'utf8', function (err,data2) {     if (err) {         return console.log("error here!: " + err);     }     englishwords = data2.split('\n');     });  // 2. combine lists //*** fails here, i'm guessing it's because readfile operation hasn't finished yet.      var combinedlist;     for(i=0; i<frenchwords.length; i++){         combinedlist[i] = frenchwords[i] + ",,," + englishwords[i];     } // 3. check result      for(i=0; i<10; i++){         console.log(combinedlist[i]);     } 

thanks help, i'm doing keep mind active :-)

you correct asynchronous nature of fs.readfile() callbacks causing issue.

those callbacks called @ indeterminate time in future while rest of code continues run. because of event driven design of node.js, callbacks won't called until rest of code finishes executing. therefore, guaranteed trying use englishwords , frenchwords variables before have results in them.

you have bunch of different options:

switch fs.readfileasync (not recommended in cases)

you can switch using fs.readfilesync(). simplest change because current flow of control work. but, not recommended node.js development because it's inefficient server usage. if code in server process could/should maintain ability other things while waiting file read, fs.readfilesync() kill scalability. if one-off script (not loaded server), fs.readfilesync() might work fine. but, should learn better "node.js-style" wait of coding async operations (see following options).

serialize operations continuing flow inside callback

you can serialize async operations nesting. involves continuing processing logic inside async callbacks. way, know result need available continues processing. this:

const fs = require('fs')  // read french file fs.readfile('frenchverbslist.txt', 'utf8', function (err, data) {     if (err) {         return console.log("error here!: " + err);     }     var frenchwords = data.split('\n');      //read english file     fs.readfile('englishverbslist.txt', 'utf8', function (err, data2) {         if (err) {             return console.log("error here!: " + err);         }         var englishwords = data2.split('\n');          // 2. combine lists         var combinedlist = [];         (i = 0; < frenchwords.length; i++) {             combinedlist[i] = frenchwords[i] + ",,," + englishwords[i];         }          // 3. check result         (i = 0; < 10; i++) {             console.log(combinedlist[i]);         }     }); }); 

manually code check when both async operations done

the serialize option above has disadvantage in waits first async operation done before starts next async operation. less ideal because both async operations running in parallel (faster end-result). of following options different ways of running async operations in parallel , monitoring when both done can trigger final processing. manual monitoring option. in completion callback both loading of french , english words, check see if other done. if is, call function process results. since 1 can complete @ time, long there no errors, 1 of them complete second , call function process results:

var frenchwords, englishwords; fs = require('fs')  // read french file fs.readfile('frenchverbslist.txt', 'utf8', function (err, data) {     if (err) {         return console.log("error here!: " + err);     }     frenchwords = data.split('\n');     if (frenchwords && englishwords) {         processresults();     } });  //read english file  fs.readfile('englishverbslist.txt', 'utf8', function (err, data2) {     if (err) {         return console.log("error here!: " + err);     }     englishwords = data2.split('\n');     if (frenchwords && englishwords) {         processresults();     } });  function processresults() {      // combine lists     var combinedlist = [];     (let = 0; < frenchwords.length; i++) {         combinedlist[i] = frenchwords[i] + ",,," + englishwords[i];     }      // check result     (let = 0; < 10; i++) {         console.log(combinedlist[i]);     } } 

use es6 promises monitor async operations

with es6, promises have become standard part of javascript specification , excellent way coordinate multiple asynchronous operations , make proper error handling (especially in complex situations) lot more straightforward. use promises here, first want create "promisified" version of fs.readfile(). wrapper function uses promises instead of plain callback. then, can use promise.all() coordinate when 2 async operations done.

var fs = require('fs'); // promise wrapper fs.readfileasync = function(file, encoding) {     return new promise(function(resolve, reject) {         fs.readfile(file, encoding, function(err, data) {             if (err) return reject(err);             resolve(data);         });             }); }  // common helper function function readfilesplitwords(file) {     return fs.readfileasync(file, 'utf8').then(function(data) {         // make split words fulfilled value of promise         return data.split('\n');     }); }  var frenchpromise = readfilesplitwords('frenchverbslist.text'); var englishpromise = readfilesplitwords('englishverbslist.txt'); promise.all([frenchpromise, englishpromise]).then(function(results) {     // combine lists     var frenchwords = results[0], englishwords = results[1];     var combinedlist = [];     (i = 0; < frenchwords.length; i++) {         combinedlist[i] = frenchwords[i] + ",,," + englishwords[i];     }      // check result     (i = 0; < 10; i++) {         console.log(combinedlist[i]);     } }, function(err) {    // handle error here }); 

use promise library extended promise functionality

es6 promises capable, there helpful features when using promises 3rd party libraries have added. use bluebird library. here's how previous option using bluebird library:

const promise = require('bluebird'); const fs = promise.promisifyall(require('fs'));  // common helper function function readfilesplitwords(file) {     return fs.readfileasync(file, 'utf8').then(function(data) {         // make split words fulfilled value of promise         return data.split('\n');     }); }  var frenchpromise = readfilesplitwords('frenchverbslist.text'); var englishpromise = readfilesplitwords('englishverbslist.txt'); promise.all([frenchpromise, englishpromise]).spread(function(frenchwords, englishwords) {     // combine lists     var combinedlist = [];     (i = 0; < frenchwords.length; i++) {         combinedlist[i] = frenchwords[i] + ",,," + englishwords[i];     }      // check result     (i = 0; < 10; i++) {         console.log(combinedlist[i]);     } }, function(err) {    // handle error here }); 

this uses bluebird's promise.promisifyall() automatically make promisified versions of methods in fs library (very useful). and, uses .spread() method instead of .then() automatically separate out 2 results named arguments.

use more extended features process arbitrary array of filenames

you can use more extended bluebird features such promise.map() processes array , promise.all() on resulting promises (something above code did manually). this, allows make filenames arbitrary list of filenames of whatever languages want , code can made more generic in regard:

const promise = require('bluebird'); const fs = promise.promisifyall(require('fs'));  // common helper function function readfilesplitwords(file) {     return fs.readfileasync(file, 'utf8').then(function(data) {         // make split words fulfilled value of promise         return data.split('\n');     }); }  var files = ['frenchverbslist.text', 'englishverbslist.txt']; promise.map(files, readfilesplitwords).then(function(results) {     // results array of arrays each sub-array language list of words     // combine lists (assumes word lists have same length)     var combinedlist = [];     var len = results[0].length;     // each word in first array     (var = 0; < len; i++) {         // other words in same array position         var words = [];         (var j = 0; j < results.length; j++) {             words.push(results[j][i]);         }         combinedlist.push(words.join(',,,'));     }      // check result     (i = 0; < 10; i++) {         console.log(combinedlist[i]);     } }, function(err) {    // handle error here }); 

Comments

Popular posts from this blog

ios - RestKit 0.20 — CoreData: error: Failed to call designated initializer on NSManagedObject class (again) -

java - Digest auth with Spring Security using javaconfig -

laravel - PDOException in Connector.php line 55: SQLSTATE[HY000] [1045] Access denied for user 'root'@'localhost' (using password: YES) -