Depa
Depa

Reputation: 509

make a async calls inside a for synchroniously

I am having some trouble with asynchronous events in sails.js.

I'am getting some data from an JSONapi and trying to write them into my database inside a for loop. Everything needs to be executed one after another (in the correct order). To keep the example simple lets just say i am trying to do the following:

//let apiNames be a JSON that contains some names in that order: James, Betty, Jon
//let Customer be a DB which contains that names and related age

for(index in apiNames){
   console.log("Searching for "+apiNames[index].name+" in the Database...);
   Customer.find({name:apiNames[index].name}).exec(function(err,customerData){
      console.log("Found "+apiNames[index].name+" in the Database!");
      console.log(customerData);
   });
}

The suggesting log should look like this:

Searching for James in the Database....
Found James in Database!
{name:James, age:23}

Searching for Betty in the Database....
Found Betty in Database!
{name:Betty, age:43}

Searching for Jon in the Database....
Found Jon in Database!
{name:Jon, age:36}

Since Javascript is running asynchronously and the DB call is taking to long, the output looks something like this:

Searching for James in the Database....
Searching for Betty in the Database....
Searching for Jon in the Database....
Found James in Database!
{name:James, age:23}
Found Betty in Database!
{name:Betty, age:43}
Found Jon in Database!
{name:Jon, age:36}

I already tried several things to force the loop working synchronously, but nothing worked. AFAIK if i call something inside exec, it should run synchronously (By Chaining it with another exec), but my problem is, that its already failing working synchroniously at the loop. Does anyone have a solution for this and could explain it a bit?

Edit: apiNames is not an array, its a JSON with some datas inside. Here is an example how the apiNames looks like:

[{
   "name": "James",
   "gender": "male"
},
{
   "name": "Betty",
   "gender": "female"
},
{
   "name": "Jon",
   "gender": "male"
}]

(Added gender to have some more information in the json. Its unimportant for the solution)

Upvotes: 2

Views: 135

Answers (3)

Molda
Molda

Reputation: 5704

Since i believe we talk about nodejs here i would use async.js

and apiNames looks like array to me and if it's not then make it

var async = require('async');

var apiNames = JSON.parse('[{"name": "James","gender": "male"},{"name": "Betty","gender": "female"}]');

async.eachSeries(apiNames, function(apiName, callback) {

  console.log("Searching for "+apiName.name+" in the Database...);

  Customer.find({name:apiName.name}).exec(function(err,customerData){
    console.log("Found "+apiName.name+" in the Database!");
    console.log(customerData);
    callback();
  });

});

Upvotes: 0

Levi Mootz
Levi Mootz

Reputation: 306

You can use recursion to test booleans which you can switch when you're ready.

var isFinding = false;
var found = [];

for (index in apiNames) {
  checkIndex(index);
}

function checkIndex (index) {
  if (index) {
    found[index - 1]
    ? findCustomer(index)
    : setTimeout(checkIndex, 100, index);
  } else {
    findCustomer(index);
  }
}

function findCustomer (index) {
  if (isFinding) {
    setTimeout(findCustomer, 100, index) 
  } else {
    isFinding = true;
    console.log("Searching for "+apiNames[index]+" in the Database...");
    Customer.find({name:apiNames[index]}).exec(function(err,customerData){
      console.log("Found "+apiNames[index]+" in the Database!");
      console.log(customerData);
      found[index] = true;
      isFinding = false;
    });
  }
}

console.log("Searching for "+apiNames[index]+" in the Database...");

This is untested and I'm half-asleep...but I'm pretty sure this should work how you want...or at least get you close :-)

Upvotes: 0

Arun P Johny
Arun P Johny

Reputation: 388316

Since apiNames is an object, for IE9+ compatibility we can use Object.keys() to get the key names in the object and use that to iterate over the apiNames

//process all names in the array one by one
function process(apiNames, keys) {
    //if there are no items in the array return from the function
    if (!keys.length) {
        return;
    }
    //get the first name in the array
    var key = keys.shift();
    var name = apiNames[key];
    console.log("Searching for " + name + " in the Database...");
    Customer.find({
        name: name
    }).exec(function (err, customerData) {
        console.log("Found " + name + " in the Database!");
        console.log(customerData);
        //once the current item is processed call the next process method so that the second item can be processed
        process(apiNames, keys);
    });
}

//call the process method with an array
var keys = Object.keys(apiNames);
process(apiNames, keys);

For older browsers use a polyfill to add support for Object.keys() like the one provided by MDN

Demo: Fiddle

Upvotes: 3

Related Questions