here is the structure
var invalid = false;
async.series([
function(callback)
{
someAsync.do(something, function(data)
{
if(data == null) invalid = true;
callback();
}
},
function(callback)
{
if(invalid) callback();
someAsync.doSomethingImportant(something, function(data)
{
if(data == null) invalid = true;
callback();
}
], function()
{
if(invalid) doThis();
else doThat();
});
The problem is that the second async function NEEDS that first async has worked successfully.
Sometimes async.series calls the callback() before the statement invalid = true and crashes my app. Also, sometimes it works.
Could you help me?
Thanks!
Your code looks correct, but if you are depended on the result of the first function, you could try using async.waterfall:
async.waterfall([
function(callback)
{
someAsync.do(something, callback);
},
function(data, callback)
{
if(data == null) {
callback(new Error('no data'));
} else {
someAsync.doSomethingImportant(something, callback)
}
},
function (data, callback) {
if (data == null) {
callback(new Error('no data'));
} else {
callback();
}
}
], function(err)
{
if (err) {
doError();
} else {
doThat();
}
});
Problem solved!
Instead of using my own error control, I used it's default:
async.series([
function(callback)
{
someAsync.do(something, function(data)
{
if(data == null) callback(true);
else callback();
}
},
function(callback)
{
someAsync.doSomethingImportant(something, function(data)
{
if(data == null) callback(true);
else callback();
}
], function(invalid)
{
if(invalid) doThis();
else doThat();
});
If you pass callback(err), it will skip all other asyncs and execute the callback.
Instead of:
[function a () { async_1(); },
function b () { async_2(); }]
function c () {}
what you need is
function a () {
async_1(function (data) {
function b (data) {
async_2(function (result) {
function c (success) { console.log(success); }
c(result);
});
}
b(data);
});
}
Now there is no race.
When a finishes, call b, when b finishes, call c.
This can get really ugly, so tools like Promises are used to turn this into:
async_1()
.then(async_2())
.then(c);
It might not really be that clean, if you need to pass data around (and not just wait for each one to be finiahed) in turn)... ...but promises are very powerful for this kind of work.
Related
I have an ajax call which return some data. Code is:
function ProductCodeLookUp() {
return $.ajax({
url: "/ProductInventory/GetProductCodeHirerchy",
type: "get", //send it through get method
async: false,
data: {
productCode: $('#ProductCodeText_Id').val()
},
success: function (response) {
debugger;
if (response != null && (response.tissueClassificationType != null || response.tissueClassificationType != null)) {
$('#productCodeTable_id').show();
$('#ProductCodeNotExistsWarning_id').hide();
//addOptionsToSelect(response, 'size_id', $('input[id=size_id]').val());
$('#ProductCodeTextClassification_Id').text(response.tissueClassificationType);
$('#ProductCodeTextTissueType_Id').text(response.tissueType);
$('#ProductCodeTextProduct_Id').text(response.tissueDefinition);
$('#ProductCodeTextSize_Id').text(response.tissueDefinitionSize);
}
else {
$('#ProductCodeNotExistsWarning_id').show();
$('#productCodeTable_id').hide();
}
},
error: function (xhr) {
//Do Something to handle error
}
});
}
I Have to take some decision on the basis of the data returned by this Ajax call. The method where i am calling ProductCodeLookUp() method is:
async function checkRequiredFields() {
var isValid = false;
if (checkRequiredFieldsSubMethod() == true) {
if (AddProductInBulk() == true) {
if (QualityChecksValidation() == true) {
debugger;
var res = await ProductCodeLookUp();
if (res.tissueDefinitionSize != null)
isValid = true;
else
isValid = false;
}
}
}
debugger;
return isValid;
}
Issue is that it does not await on the ProductCodeLookUp() method. I have to wait for the result of this method and on the basis of result make a decision that whether to return true or false. But i am unable to do this. What can be the possible issue.
Wrap the ajax call in a promise that resolves on success and rejects on error.
function ProductCodeLookUp() {
return new Promise((resolve, reject) => $.ajax({
// ...
success: function (response) {
// ...
resolve();
},
error: function (response, status, err) {
// ...
reject(err);
}
}));
}
Also, set async to true
I have a nodejs script that gets some json data containing item names and prices using an API, then checks the price of that item. The problem I have is that the function that checks the item name gets executed before the function that writes the price list ends. So if the price list file doesn't exist, it gives me 'no such file or directory' for the price list file. I searched for some time on the internet, and I found something about async.series. I tried something, but it doesn't seem to work, since the result is the same. If the price list file exists, the message 'Prices updated successfully! Lenght:' appears after printing the item price.
function getPrices() {
console.log("URL requested: ", API_URL)
restling.get(API_URL).then(function(result) {
var JSON_Data = result.data
if (JSON_Data.status == "success") {
console.log("Prices updated successfully! Lenght: "+JSON_Data.prices.length)
} else {
console.log(JSON_Data)
console.log("An error ocurred during updating prices!")
return
}
fs.writeFileSync("prices/pricelist.txt", JSON.stringify(JSON_Data.prices))
})
}
function getItemPrice(item) {
var file = JSON.parse(fs.readFileSync("prices/pricelist.txt"))
for (var i = 0; i < file.length; i++) {
if (file[i].item_name == item) {
return file[i].price
}
}
}
function getItem() {
var item1 = getItemPrice('Sword');
console.log(item1);
}
async.series([
function(callback){
getPrices();
callback();
},
function(callback){
getItem();
callback();
}
]);
EDIT : I've tried something else, but the problem remains the same
async.series([
function(callback){
getPrices();
callback();
},
function(callback){
getItem();
callback();
}
], function(error){
if (error) {
console.log(error);
}
});
async.waterfall([
function(callback){
getPrices();
callback();
},
function(arg1, callback){
getItem();
callback();
},
], function (error) {
if (error) {
console.log(error);
}
});
Mmm, i think not makes so much sense write the response in a file for read later, since you can use directly the response to find the item, but this should works.
function getPrices(callback) {
restling.get(API_URL).then(function(result) {
var JSON_Data = result.data
if (JSON_Data.status == "success") {
console.log("Prices updated successfully! Lenght: " + JSON_Data.prices.length)
} else {
console.log(JSON_Data)
console.log("An error ocurred during updating prices!")
return
}
fs.writeFileSync("prices/pricelist.txt", JSON.stringify(JSON_Data.prices))
callback(null, null);
});
}
function getItemPrice(item) {
var file = JSON.parse(fs.readFileSync("prices/pricelist.txt"))
for (var i = 0; i < file.length; i++) {
if (file[i].item_name == item) {
return file[i].price
}
}
}
function getItem(callback) {
var item1 = getItemPrice('Sword');
callback(null, item1);
}
async.series([
getPrices,
getItem
],
function(err, result) {
if (err) {
console.log(err);
return;
}
console.log("result", result);
});
I think a promises based approach and manipulating the response directly instead of writing / reading form a file , will be easier to understand.
function getPrices(url) {
return new Promise( function(resolve) {
reslint.get(url).then( function(result) {
var data = result.data;
if(data.status === "success") {
console.log(
"Prices updated successfully! Lenght: ",
data.prices.length
);
return resolve(data.prices);
}
else {
throw new Error("An error ocurred during updating prices!");
}
})
.catch(function(ex) {
throw ex;
});
});
}
function getItemPrice(item, items) {
for (var i = 0; i < items.length; i++) {
if (items[i].item_name == item) {
return items[i].price
}
}
}
getPrices("some/url/which/return/prices")
.then(function(prices) {
console.log(getItemPrice("Sword", prices));
})
.catch(function(err) {
console.log("some error -->", err);
});
If I am not mistaken doesn't series expect a final callback.
async.series([
function(callback){
getPrices();
callback();
},
function(callback){
getItem();
callback();
}
],
function(error, [callback array]){
});
Where the callback array will contain the result returned from the functions array. Maybe try async.waterfall?
I'm very new to nodejs's async.forEach and I'm having trouble aggregating the result of my nested forEach loop.
I have a dynamic range of dates and a number of screens that I would like to loop through and either create a schedule or update an existing one. That part works as designed. However, I can't get construct an array of all schedules that has been created and have been updated. I seem to only get the first one but not the rest.
I've tried many different ways of calling the callback but the most I've ever gotten is just one item in my output array.
I've tried different methods from this website but I haven't gotten the luck:
http://www.sebastianseilund.com/nodejs-async-in-practice
What is the best way of handling this scenario?
Below is my trimmed down loopback remoteMethod:
===========================
Schedule.Reservation = function(PostData, cb) {
var output = []; // <-- I would like to return this array ... which is report of all created and updated schedules
try {
// create all models
async.series([
function validateData(callback) {
callback();
},
function processReservation(callback) {
var screens = PostData.Screens;
var dates = getDateRangeArray(PostData);
async.forEach(dates, function(liveDate, callbackDate)
//for (var d = new Date(PostData.StartDate); d <= end; d.setDate(d.getDate() + 1))
{
async.forEach(screens, function(screen, callbackScreen)
//for (var s=0;s<screens.length;s++)
{
if (screen.details)
async.forEach(screen.details.BookingInformation, function(frame, callbackFrame) {
if ((frame.BlockedDays == 0) || (!isBlocked)) {
Schedule.findOne({
where: {
LiveDate: liveDate,
ScreenID: screen.id,
FrameID: frame.FrameID,
Remaining: {
gte: PostData.RequiredSlots
}
}
}, function(errSchedule, schedule) {
var scheduleLog = {}
scheduleLog.liveDate = liveDate;
scheduleLog.ScreenID = screen.id;
scheduleLog.FrameID = frame.FrameID;
if (!errSchedule) {
if (!schedule) {
var tempSchedule = {
LiveDate: liveDate,
Posts: "posts",
Remaining: remain
}
Schedule.create(tempSchedule,
function(err, result) {
if (err) {
output.push({
'Failed': scheduleLog,
'Error': err
});
//callbackFrame(output);
} else {
output.push({
'Created': scheduleLog,
'Success': result
});
//callbackFrame(output);
}
});
} else {
schedule.Remaining--;
schedule.save(function(err, result) {
if (err) {
output.push({
'Failed': scheduleLog,
'Error': err
});
//callbackFrame(output);
} else {
output.push({
'Updated': scheduleLog,
'Success': result
});
//callbackFrame(output);
}
});
} else {
output.push({
'Skipped': scheduleLog,
'Warning': 'Warning: Unable to update. Validation failed. ' + schedule
});
//callbackFrame(output);
}
}
} else {
output.push({
'Skipped': scheduleLog,
'Error': errSchedule
});
//callbackFrame(output);
}
}
);
}
},
function(result) {
if (output)
callback(output);
else
callbackScreen();
});
else {
throw new Error("Invalid Data");
return callbackScreen(output); //should throw an error.
}
},
function(result) {
if (output)
callbackDate(output);
else
callbackDate(output);
});
},
function(result) {
if (output)
callback(output);
else
callback();
});
//callback(output);
}
],
function(result) {
if (output) //also tried result, the outcome is the same.
{
cb(null, output);
} else
cb("Failed!!!");
});
} catch (ex) {
console.log(ex.message);
cb('!Error! ' + ex.message);
}
Do you use caolin's async library? See this link to get an idea on how to proceed: https://github.com/caolan/async#seriestasks-callback
Do not enclose your async code inside a try-catch as the async series/forEach provide their own way to handle any error. Typically any async callback accepts 2 parameters: error, result.
callbackScreen or callbackDate must call callback (second handler) to pass some result over in order to hook up your final series' callback (the one you declare at the same level of async.series).
async.series([
function A(callback){
// do some stuff ...
callback(null, 'abc'); //first arg is err. If not null you'll go to the final handler below
},
function B(callback){
// do some more stuff ...
async.forEach(dates, function(liveDate, callbackDate) {
//stuff to do with dates
callbackDate(null, 'your data'); //will go to the handler right below
}, function (err, data) {
if (err) console.error(err.message);
//here call callback of function B
callback(null, data'); //first arg is err
}))
}
],
// optional callback
function(err, results){
// results is now equal to ['abc', 'your data']
});
I'm learning node.js right now and I'm having troubles calling back.
I looked at Event Emitter but it doesn't seems to be relevant for me.
This is what I'm calling:
exports.search = function(lat, lng, Arr, callback) {
//something
geocoder.reverse({
lat: lat,
lon: lng
}, function(err, res, callback) {
//finding the area
if (area !== "null") {
pool.getConnection(function(err, connection, callback) {
if (err) {
} else {
connection.query("SOME SQL CODE", function(err, rows, fields, callback) {
if (found what Im looking
for) {
connection.query("SOME SQL CODE", function(err, rows, fields, callback) { //looking for something else
if (err) {
callback(true);
} else {
if (rows[0] === undefined) {
callback(true);
} else {
console.log("found!");
callback(null, rows[0]);
}
}
});
} else if (err) {
}
});
}
});
} else {
}
});
};
I'm getting my "found!" in the console log, but the callback doesn't working for some reason.
If I put a callback at the end of the function " search " it does call back, do I know I'm not having a problem with the function who gets the callback.
Thank you!
I think the callback(null, rows[0]) is call back of function geocoder.reverse. You can change name callback of search function is callback1 and then call as below.
exports.search = function(lat, lng, Arr, callback1) {
//something
geocoder.reverse({
lat: lat,
lon: lng
}, function(err, res, callback) {
//finding the area
if (area !== "null") {
pool.getConnection(function(err, connection, callback) {
if (err) {
} else {
connection.query("SOME SQL CODE", function(err, rows, fields, callback) {
if (found what Im looking
for) {
connection.query("SOME SQL CODE", function(err, rows, fields, callback) { //looking for something else
if (err) {
callback(true);
} else {
if (rows[0] === undefined) {
callback(true);
} else {
console.log("found!");
callback1(null, rows[0]);
}
}
});
} else if (err) {
}
});
}
});
} else {
}
});
};
You can apply async lib to your code so that your code is clear too.
It's better to use a library that helps you: like async or q
I am trying to implement a basic function using promises in one of my controllers just so I can ensure it is working correctly before adding in more complex functionality. I am getting a "TypeError: undefined is not a function" on the ".then(function(data){" in the lockPromise method.
Function called from view
$scope.lockPromise = function(fieldId) {
$scope.getLockMessage2(fieldId).getWeather()
.then(function(data) {
if (data === "returned SUCCESS info") {
alert("data is good");
} else {
alert("FAILED");
}
}, function(error) {
alert(error);
});
};
Second function in ctrl
$scope.getLockMessage2 = function(fieldId) {
return{
getWeather: function() {
return $http.get('/api/getData')
.then(function(response) {
if (typeof response.data === "string") {
return response.data;
} else {
return $q.reject(response.data);
}
}, function(response) {
return $q.reject(response.data);
});
}
};
};
API GET
[Route("api/getData")]
public HttpResponseMessage GetData()
{
string data = JsonConvert.SerializeObject("returned SUCCESS info");
return new HttpResponseMessage
{
Content = new StringContent(data, Encoding.UTF8, "application/json")
};
}
EDIT 1:
code updated to reflect comments
Change
$scope.getLockMessage2(fieldId).then
to
$scope.getLockMessage2(fieldId).getWeather().then
Your $scope.getLockMessage2 return an object, not function.
I think the code should be (not tested):
$scope.lockPromise = function(fieldId) {
$scope.getLockMessage2(fieldId).getWeather()
.then(function(data) {
if (data === "good") {
alert("data is good");
} else {
alert("FAILED");
}
}, function(error) {
alert(error);
});
};