Codex
Codex

Reputation: 589

Node JS : Array loop and function Not understanding how callbacks and promises work

New to concept of callbacks and promises. I'm trying to read contents of a directory which is correctly returning the addresses but this code is only printing console.log(value) and the console.log in the function getAccount "gettin info..." but nowhere printing the balance the api is getting closed and the process completes, I dont understand this because the address is being passed and the first console.log is printing inside the function but not doing further job. when i remove the fs.readdir and files.foreach and pass a single value to getAccount its working perfectly fine. No errors or bugs , its a runtime error and im guessing related to callbacks

'use strict';
const RippleAPI = require('ripple-lib').RippleAPI;
const testFolder = '/home/ripple/.keystore';
const fs = require('fs');
const api = new RippleAPI({server: 'wss://s1.ripple.com'});

function getAccount(address) {
        var balance = 0.0;
        console.log("getting info  for :"+address);
        return api.getAccountInfo(address).then(info => {
        var balance = info.xrpBalance;
        console.log("balance of "+address+" is "+balance);
        return balance;
        });
}

api.connect().then(() =>  {
        fs.readdir(testFolder, function(err, files) {
//      console.log("Total no of wallets : "+files.length);
//      for (var i =3000, len=3200; i < len; i++) {
//              var ad = files[i];
//              console.log(ad + "\t" + typeof(ad));
//              if(!xwallets.includes(ad))
                files.forEach(function(value) {
                        getAccount(value).then(balance => {console.log(balance); });
                        console.log(value);
                });
//              console.log("running for index : "+i+" filedata :"+files[i]);
//      }
        });
//      console.log(balance);
}).then(() => {
        return api.disconnect();
}).then(() => {
        console.log("done and disconnected");
}).catch(console.error);

Upvotes: 2

Views: 134

Answers (1)

jfriend00
jfriend00

Reputation: 707876

You really don't want to mix asynchronous operations that return a promise with those that take a direct callback. It's hard to write good robust code when mixing like that. So, since you are already using with promises in a number of operations, it's best to take the remaining async operations and "promisify" them so you can use them with promises. Then, you can take advantage of all the clean error propagation and chaining of promises.

Then, secondly, you have to make sure all your asynchronous operations in a loop are properly chained to the parent promise. To do that here, we will accumulate the promises from getAccount() in the loop into an array of promises. Then, after the loop, we can tell when they're all done using Promise.all() and then return that promise from readdir() so that they will all be properly chained to the parent promise.

This will then make sure everything inside the loop finishes before the parent resolves and before you call api.disconnect().

Here's what that would look like:

'use strict';
const RippleAPI = require('ripple-lib').RippleAPI;
const testFolder = '/home/ripple/.keystore';
const fs = require('fs');
const api = new RippleAPI({server: 'wss://s1.ripple.com'});

function getAccount(address) {
    var balance = 0.0;
    console.log("getting info  for :"+address);
    return api.getAccountInfo(address).then(info => {
        var balance = info.xrpBalance;
        console.log("balance of "+address+" is "+balance);
        return balance;
    });
}

// promisify readdir
const readdir = util.promisify(fs.readdir);

api.connect().then(() =>  {
    return readdir(testFolder).then(function(files) {
        const promises = [];
        files.forEach(function(file) {
            console.log(file);
            promises.push(getAccount(file).then(balance => {
                console.log(balance); 
                // make balance be the resolved value for this promise
                return balance;
            }));
        });
        // make sure we are waiting for all these promises to be done
        // by chaining them into the parent promise
        return Promise.all(promises);
    });
}).then(balances => {
    // process balances array here
    console.log(balances);
    return api.disconnect();
}, err => {
    // disconnect, even in the error case, but preserve the error
    console.error(err);
    return api.disconnect().then(() => {throw err;})
}).then(() => {
    console.log("done and disconnected");
});

Summary of changes made:

  1. Promisify fs.readdir() so we can use promise with it
  2. Change how we call readdir() to use promises
  3. Collect getAccount() promises inside of .forEach() into an array of promises
  4. Add return Promise.all(promises) so that we wait for all those promises to be done and so that it is chained into the parent promise
  5. Make balance be the resolved value of each promise in the .forEach() loop, even after logging its value
  6. Make sure we're calling api.disconnect() even in the error cases
  7. After logging error, preserve the error value as the reject reason

Upvotes: 1

Related Questions