Reputation: 509
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
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
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
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