Below I have two functions. One of the returns a promise and I want in each and every loop to wait for it to finish first and then move one. As you'll see below the result is not the desired/synchronous. Should I not use async.forEachof
function test(num, callback)
{
console.log("inside function: "+num)
return new Promise((resolve, reject) => {
my.promise(num).then(() => {
console.log("done: "+num)
resolve(callback);
}).catch(e => console.error(e));
}).catch(console.error);
}
function start_function()
{
//stuff
async.forEachOf(arr, function (itm, i, callback){
console.log(i)
test(i, callback).catch(e => console.error(e));
}, function (err) {
console.log("ALL DONE")
});
}
Result:
0
inside function 0
1
inside function 1
2
inside function 2
done: 0
done: 2 //wrong should always be 1, finished before the previous promise.
done: 1 //wrong
ALL DONE
I do not want to use PromiseAll not because I dont want but because I need each promise to wait for the previous one's result, and then start the next one. It is pointless therefore to gather them all and then wait for them to be done in whatever order and to what I need to do.
If I'm not wrong what exactly you are trying to do, but you should avoid passing callback in Promise function and resolving it. You can achieve the same with this code and remove callback from test function as well.
function start_function()
{
//stuff
arr.forEach(async function (itm, i, array){
console.log(i)
try {
await test(i);
}
catch(err){
console.log(err);
}
});
}
Here's how test function may look like,
function test(num)
{
console.log("inside function: "+num)
return new Promise((resolve, reject) => {
my.promise(num).then(() => {
console.log("done: "+num)
resolve();
}).catch(e => {
console.error(e)
reject(e);
});
});
}
What about this approach:
(function loopOnItems(i) {
dummyGoogleAPI.someAsyncMethod(data).then(function (_response) {
// do something with response
})
.catch(function (error) {
console.error(error);
});
if (--i){
loopOnItems(i);
}// decrement i and call loopOnItems again if i > 0
else{
_callback(errors);
}
})(count);
You have some count and we call loopOnItems method till count > 0
Related
Today, when I was working with node, I met some special async functions with "overloads" that accept both promises and callbacks. Like this:
doSomething(result => {
console.log(result)
})
doSomething()
.then(result => console.log(result))
And probably this:
const result = await doSomething()
console.log(result)
I tried to implement this in my code but was unsuccessful. Any help would be appreciated.
You can make a function like this by creating a promise, then chaining on that promise with the argument if there is one, and then returning that chained promise. That will make it call the callback at the appropriate time, as well as giving you access to the promise that will complete when the callback completes. If you want the original promise even when there's a callback (not the chained version), then you can return that instead, by still chaining but then returning the original promise instead.
function f(cb) {
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve(123), 1000);
});
if (cb) {
return promise.then(cb);
} else {
return promise;
}
}
// usage 1
f(console.log)
// usage 2
f().then(console.log)
Let's say you had a function that was going to read your config file and parse it and you wanted to support both versions. You could do that like this with two separate implementation inside. Note, this has full error handling and uses the nodejs calling convention for the callback that passes parameters (err, result):
function getConfigData(filename, callback) {
if (typeof callback === "function") {
fs.readFile(filename, function(err, data) {
try {
if (err) throw err;
let result = JSON.parse(data);
callback(null, result);
} catch(e) {
callback(err);
}
});
} else {
return fs.promises.readFile(filename).then(data => {
return JSON.parse(data);
}).
}
}
This could then be used as either:
getConfigData('./config.json').then(result => {
console.log(result);
}).catch(err => {
console.log(err);
});
configData('./config.json', (err, data) => {
if (err) {
console.log(err);
} else {
console.log(data);
}
});
Depending upon the specific asynchronous operation, it may be better or more efficient to have two separate implementations internally or to have one implementation that you adapt at the end to either a callback or a promise.
And, there's a useful helper function if you adapt a promise to a callback in multiple places like this:
function callbackHelper(p, callback) {
if (typeof callback === "function") {
// use nodejs calling convention for callbacks
p.then(result => {
callback(null, result);
}, err => {
callback(err);
});
} else {
return p;
}
}
That lets you work up a simpler shared implementation:
function getConfigData(filename, callback) {
let p = fs.promises.readFile(filename).then(data => {
return JSON.parse(data);
});
return callbackHelper(p, callback);
}
Here's my code
function getprefix(guildid) {
con.connect(function(err) {
con.query('SELECT * from ' + configcontent.guilds_table_name + ' WHERE guild_id=' + String(guildid), function(err, result) {
if (err) {
throw err;
} else {
return result[0].prefix;
}
});
});
}
I wanted to make this function to return one value from one column (from first result), but it returns undefined. When i tried to write it on screen (using console.log(result[0].prefix);) it works.
Please help. Sorry for bad description or english. Its my first question on StackOverflow
Your function is asynchronous, so it immediately returns result, now it returns undefined. Value from DB returned later, and passed to callback. You need to use promises(preferred)/callbacks to return async results from your function, something like this:
function getprefix(guildid) {
return new Promise((resolve, reject) => {
con.connect(function (err) {
con.query('SELECT * from ' + configcontent.guilds_table_name + ' WHERE guild_id=' + String(guildid), function (err, result) {
if (err) {
// throw err;
// instead of throwing we passing error to result promise
reject(err)
} else {
// return result[0].prefix;
// instead of return we pass result to promise
resolve(result[0].prefix)
}
});
});
}
}
so usage will look like:
getprefix(guildid)
.then(prefix => console.log(prefix))
.catch(e => console.err('Unable to get prefix', e))
it would be great to check the different ways to handle async programming in JS with callbacks/promises/async awaits in details.
You can't return value from a callback.
Before you make your query, connect to the db. Don't use the callback function for that, cuz making a query supposed to be made with con.query
function getprefix(guildid) {
con.connect();
con.query('SELECT * from ' + configcontent.guilds_table_name + ' WHERE guild_id=' + String(guildid), function(err, result) {
if (err) {
throw err;
} else {
// do stuff with 'results'
}
});
EDIT
You can use async/await like this to get access to the return value, but you have to wrap your code in an IIFE (Immediately Invoked Function Expression) or use top level await, but it's only available in the latest version of Node.
Note, that I only used IIFE, because I don't have any other function or logic where I could call my query. If you call it in another function, make it async.
// Top level action
(async () => {
const output = await makeQuery();
console.log(output);
})();
// Query
function makeQuery() {
return new Promise((resolve, reject) =>
db.query("SELECT * FROM users", (err, res) => {
if (err) reject(err);
else resolve(res);
})
);
}
I am starting to use promise in NodeJS. The requirement is to use these because of async calls. Here is the first code I wrote for promise.
function asyncFunc(data) {
return new Promise(
function(resolve, reject) {
try {
resolve(data);
} catch (err) {
reject("Custom Error");
}
});
}
//Usage:
asyncFunc('Sample String')
.then(result => { console.log(result); })
.catch(error => { console.log(error); });
//Output:
Sample String
null
//If I change the code to:
function asyncFunc(data) {
return new Promise(
function(resolve, reject) {
try {
reject("Custom Error");
} catch (err) {
resolve("Data");
}
});
}
//Output:
//Exception has occurred: string
//I get the above exception at line: reject("Custom Error");
So the question is "reject" can only be used from "catch" block? Why can't I raise "reject" event manually? What is the scenario "reject" is used? Can someone provide me a better example where I can use both "resolve" and "reject"?
You can use reject if you want, of course.
Let's suppose you have a function that resolves a promise if a person is adult, and rejects it if not.
You'd have something like this:
function asyncFunc(age) {
return new Promise((resolve, reject) => {
if(age >= 18) {
resolve(true);
} else {
// here you call reject manually outside of a catch block
reject(false);
// or
// reject(Error('not adult'));
}
});
}
usage:
asyncFunc(19).then(result => {
console.log(result); // true
})
.catch(error => {
console.log(error); // false or Error('not adult')
});
Probably you got something like (node:5009) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
Looks like you run your asyncFunc(..) in console. In this case node.js first execute your function after your hit Enter. Try to take your code to curly brackets like this:
{
asyncFunc('Sample String')
.then(result => { console.log(result); })
.catch(error => { console.log(error); });
}
In case of a try/catch block you usually reject at catch but you can also reject in try.
resolve and reject are basically callbacks for Promise. if you resolve it will got to the next chain if you reject it will break the chain.
So should use reject when an error occurs cause reject will break the promise chain.
For example.
Lets say you have a function that works with callback and you want to wrap it in a Promise like function. This function will check if user exists in database. If the User is found it will return true if not false and if there is an error in database (eg connection) it will reject.
function checkUserExist(id) {
return new Promise((resolve, reject) => {
checkUserExistInDatabase(id, function(result, error) {
if (error) {
reject(error);
}
if (result != null) {
resolve(true);
} else {
resolve(false);
}
})
});
}
function databaseError() {
return new Promise((resolve, reject) => {
reject();
})
}
var idsThatExist = [];
checkUserExist(1).then(function(exist) {
if (exist)
idsThatExist.push(1);
return checkUserExist(2)
}).then(function(exist) {
if (exist)
idsThatExist.push(2);
return databaseError(3)
}).then(function(exist) {
//WILL never Reach here
if (exist)
idsThatExist.push(3);
return checkUserExist(4)
}).then(function(exist) {
if (exist)
idsThatExist.push(4);
}).catch(function(err) {
//it will skip checkUserExist(4)
console.log('I got rejected after checked users:'
idsThatExist)
})
So the question is "reject" can only be used from "catch" block?
No, You can use reject anywhere. Catch isn't necessary to use reject
Why can't I raise "reject" event manually?
You can reject using creating new Promise() or static methods of Promise . See promiseUsingNew() and promiseUsingStaticMethod()
What is the scenario "reject" is used?
try/catch is used for error handling in synchronous programming. resolve & reject is for error handling in asynchronous programming operation instead of callbacks.
Can someone provide me a better example where I can use both "resolve" and "reject"?
'use strict';
function promiseUsingNew(marks) {
return new Promise((resolve, reject) => {
if (marks < 0 || marks > 100) {
return reject('Invalid marks');
}
if (marks >= 40) {
return resolve('You passed');
} else {
return resolve('You Failed');
}
});
}
function promiseUsingStaticMethod(marks) {
if (marks < 0 || marks > 100) {
return Promise.reject('Invalid marks');
}
if (marks >= 40) {
return Promise.resolve('You passed');
} else {
return Promise.resolve('You Failed');
}
}
// you can use promiseUsingNew(marks) or promiseUsingStaticMethod(marks)
promiseUsingNew(221).then((result) => {
console.log(result);
}).catch((error) => {
console.log(error);
});
After the underPrice function call, it goes straight to the next promise call and returns undefined, then goes to under price. I am not sure what I am doing wrong any help would be really appreciated. The data is there as undrPrice gets the keys and if I put a console.log - it prints it out. The issues is the second then statement goes off without the first one being done
/// grab all online
var getActiveData = (payload) => {
pub.smembers('partner:services:' + payload.services)
.then((data) => {
Promise.all(data.map(underPrice.bind(this, payload)))
})
.then((data) => {
Promise.all(console.log(data));
});};
Here is the underPrice code
var underPrice = (payload, key) => {
return new Promise((resolve, reject) => {
pub.hmget(key + ":services:" + payload.services, "amount", (err, data) => {
if (err) reject(err); //will display '1, 3, 5, 7'
else {
data <= payload.price ? resolve(key) : reject(key);
}
});
});
};
You are forgetting to return the promises from your functions, especially from the then callbacks, so the caller will have nothing to wait for.
function getActiveData(payload) {
return pub.smembers('partner:services:' + payload.services)
// ^^^^^^
.then(data => {
return Promise.all(data.map(underPrice.bind(this, payload)))
// ^^^^^^
})
.then(data => {
console.log(data);
return data;
// ^^^^^^
});
}
I have an array which is being iterated using Asynch.forEachSeries. Inside the iterator I find the model and updated. I need to iterate the second item after update is happened. Find the code below.
async.eachOfSeries(stockUpdate, function (valueSU, keySU, callbackSU) {
ProductVariations.findOne({id:valueSU.id}).exec(function (ePV,dPV){
dPV.available_stock = parseInt(dPV.available_stock) - Qty;
dPV.save(function (errDPV) {
callbackSU(); // HERE ONCE, NEXT ITERATOR SHOULD BE CALLED
});
});
}, function (err) {
if (err) callback(err.message);
});
I think you should wrap "ProductVariations..." in promise. And wait, until it resolves. And then call next iterator.
This code must solve you task
let promiseProductVariations = function (valueSU, keySU) {
return new Promise(function (resolve, reject) {
ProductVariations.findOne({id:valueSU.id}).exec(function (ePV,dPV){
dPV.available_stock = parseInt(dPV.available_stock) - Qty;
dPV.save(function (errDPV) {
if(errDPV){
reject(errDPV);
}else{
resolve(); // HERE ONCE, NEXT ITERATOR SHOULD BE CALLED
}
});
});
})};
async.eachOfSeries(stockUpdate, function (valueSU, keySU, callbackSU) {
promiseProductVariations(valueSU, keySU)
.then(function () {
callbackSU();
})
.catch(function (error) {
if(error)
console.log(error);
//do thomething with error
});
}, function (err) {
if (err) callback(err.message);
});