I'm trying to do something if the value is not exist, so I can update it. but isExist function always return undefined. what can I do with this?
reference: Ero is already defined.
async.forEachOf(idArray, function(value, key, cb) {
rp(baseURL + value)
.then(function(json) {
if (!isExist(json)) {
// do something
} else {
console.log(isExist(json), "It's not video or exists");
}
})
.catch(function(err) {
console.error(err);
})
cb();
}, function() {
res.status(200)
});
});
function isExist(data) {
let parsedData = JSON.parse(data);
if (parsedData.items[0].type === 'Video') {
Ero.find({
videoUri: parsedData.items[0].url_mp4
}, function(err, docs) {
if (docs.length) {
return true;
} else {
return false;
}
})
} else {
return false;
}
}
Let's look at your isExist function.
function isExist(data) {
let parsedData = JSON.parse(data);
if (parsedData.items[0].type === 'Video') {
Ero.find({
videoUri: parsedData.items[0].url_mp4
}, function(err, docs) {
if (docs.length) {
return true;
} else {
return false;
}
})
} else {
return false;
}
}
In that function you have two branches at the conditional. When the condition is false the else block will run – this returns false. When the condition is true the first block will run however there is no return statement, therefore implicitly returning undefined.
You say "why does it not have a return statement?", I'm pretty sure I have one.
It looks like you have one here.
if (docs.length) {
return true;
} else {
return false;
}
However look at which function it is returning out of. It is only returning out of the callback function passed to Ero.find, it does not return out of isExist.
You ask "what can I do about this?".
I am assuming Ero.find is an asynchronous function, therefore isExist will become an asynchronous function too. To do async functions in JavaScript you can use Promises or async functions.
Here's some example code of what isExist might look like with a Promise.
function isExist(data) {
/**
* `isExist` returns a Promise. This means the function _promises_ to have a value resolved in the future.
*/
return new Promise((resolve, reject) => {
let parsedData = JSON.parse(data);
if (parsedData.items[0].type === 'Video') {
Ero.find({
videoUri: parsedData.items[0].url_mp4
}, function(err, docs) {
if (docs.length) {
/**
* Once `Ero.find` has completed, `resolve` `isExist` with a value of `true`, otherwise `resolve` `isExist` with a value of `false`.
*/
resolve(true);
} else {
resolve(false);
}
})
} else {
/**
* You can resolve a Promise even without performing an asynchronous operation.
*/
resolve(false);
}
});
}
Further reading
Async & Performance, a book in the series You Don't Know JS by Kyle Simpson.
Promises in JavaScript
Async functions in JavaScript
Using JSON.parse will put you in a risk to have an exception if the JSON has a syntax error. Use a try / catch block.
Without knowing your data I cannot say what else is wrong on your check.
function isExists(data){
try{
var parsedData = JSON.parse(data);
if (parsedData.items[0].type === 'Video') {
Ero.find({
videoUri: parsedData.items[0].url_mp4
}, function(err, docs) {
if (docs.length) {
return true;
} else {
return false;
}
})
} else {
return false;
}
}catch(e) {
// any error
return false;
}
}
Related
I'm learning JavaScript, and I decided that an excelent chalenge would be to implement a custom Promise class in JavaScript. I managed to implement the method then, and it works just fine, but I'm having difficulties with the error handling and the method catch. Here is my code for the Promise class (in a module called Promise.mjs):
export default class _Promise {
constructor(executor) {
if (executor && executor instanceof Function) {
try {
executor(this.resolve.bind(this), this.reject.bind(this));
} catch (error) {
this.reject(error);
}
}
}
resolve() {
if (this.callback && this.callback instanceof Function) {
return this.callback(...arguments);
}
}
reject(error) {
if (this.errorCallback && this.errorCallback instanceof Function) {
return this.errorCallback(error);
} else {
throw `Unhandled Promise Rejection\n\tError: ${error}`;
}
}
then(callback) {
this.callback = callback;
return this;
}
catch(errorCallback) {
this.errorCallback = errorCallback;
return this;
}
}
When I import and use this class in the following code, all the then() clauses run as according, and I get the desired result in the console:
import _Promise from "./Promise.mjs";
function sum(...args) {
let total = 0;
return new _Promise(function (resolve, reject) {
setTimeout(function () {
for (const arg of args) {
if (typeof arg !== 'number') {
reject(`Invalid argument: ${arg}`);
}
total += arg;
}
resolve(total);
}, 500);
});
}
console.time('codeExecution');
sum(1, 3, 5).then(function (a) {
console.log(a);
return sum(2, 4).then(function (b) {
console.log(b);
return sum(a, b).then(function (result) {
console.log(result);
console.timeEnd('codeExecution');
});
});
}).catch(function (error) {
console.log(error);
});
But, when I add an invalid argument to the sum() function, i.e. not a number, the reject() method runs, but it don't stop the then() chain, as should be, and we also get an exception. This can be seen from the following code:
import _Promise from "./Promise.mjs";
function sum(...args) {
let total = 0;
return new _Promise(function (resolve, reject) {
setTimeout(function () {
for (const arg of args) {
if (typeof arg !== 'number') {
reject(`Invalid argument: ${arg}`);
}
total += arg;
}
resolve(total);
}, 500);
});
}
console.time('codeExecution');
sum(1, 3, '5').then(function (a) {
console.log(a);
return sum(2, 4).then(function (b) {
console.log(b);
return sum(a, b).then(function (result) {
console.log(result);
console.timeEnd('codeExecution');
});
});
}).catch(function (error) {
console.log(error);
});
Also, if I catch an error in nested then() methods, the outer catch() doesn't notice this and I get an exception again. The goal is to implement a lightweight functional version of Promises, but not necessarily with all its functionality. Could you help me?
The problem in your code is that your sum function calls both the reject and the resolve functions. There's no handling in the sum function that will cause it not to call the resolve function at the end, and there is nothing in your _Promise that blocks this behavior.
You have 2 options to fix this.
Option 1 would be if you want your _Promise to act like a real Promise you will need to manage a state and once a promise got to a final state stop calling the callback or errorCallback.
Option 2 would be to prevent from calling both reject and resolve in the function calling the _Promise, in this case, the sum function.
With the comments that you guys provide me, I was able to improve the code and correct the errors mentioned, as shown below. Now, I would like you to give me suggestions on how to proceed and improve the code. Thanks. (The code can also be found on github).
const PENDING = 0;
const FULFILLED = 1;
const REJECTED = 2;
function _Promise(executor) {
let state = PENDING;
let callOnFulfilled = [];
let callOnRejected = undefined;;
function resolve(...args) {
if (!state) {
state = FULFILLED;
}
resolveCallbacks(...args);
};
function reject(error) {
state = REJECTED;
if (callOnRejected && (callOnRejected instanceof Function)) {
callOnRejected(error);
callOnRejected = undefined;
callOnFulfilled = [];
} else {
throw `Unhandled Promise Rejection\n\tError: ${error}`;
}
};
function resolveCallbacks(...value) {
if (state !== REJECTED) {
let callback = undefined;
do {
callback = callOnFulfilled.shift();
if (callback && (callback instanceof Function)) {
const result = callback(...value);
if (result instanceof _Promise) {
result.then(resolveCallbacks, reject);
return;
} else {
value = [result];
}
}
} while (callback);
}
};
if (executor && (executor instanceof Function)) {
executor(resolve, reject);
}
this.then = function (onFulfilled, onRejected) {
if (onFulfilled) {
callOnFulfilled.push(onFulfilled);
if (state === FULFILLED) {
resolveCallbacks();
}
}
if (onRejected && !callOnRejected) {
callOnRejected = onRejected;
}
return this;
};
this.catch = function (onRejected) {
return this.then(undefined, onRejected);
};
}
function sum(...args) {
let total = 0;
return new _Promise(function (resolve, reject) {
setTimeout(function () {
for (const arg of args) {
if (typeof arg !== 'number') {
reject(`Invalid argument: ${arg}`);
}
total += arg;
}
resolve(total);
}, 500);
});
}
console.time('codeExecution');
sum(1, 3, 5).then(function (a) {
console.log(a);
return sum(2, 4).then(function (b) {
console.log(b);
return sum(a, b).then(function (result) {
console.log(result);
return 25;
});
}).then(function (value) {
console.log(value);
console.timeEnd('codeExecution');
});
}).catch(function (error) {
console.log(error);
});
Sorry if my title is not very explicit I dont know how to explain this properly.
I am trying to use the distinct function for my app that uses loopback 3 and mongodb. It seems to work right but my endpoint wont hit the return inside my function.
This is my code
const distinctUsers = await sellerCollection.distinct('userId',{
hostId : host.id,
eventId:{
"$ne" : eventId
}
}, async function (err, userIds) {;
if(!userIds || userIds.length ==0)
return [];
const filter = {
where:{
id: {
inq: userIds
}
}
};
console.log("should be last")
return await BPUser.find(filter);
});
console.log(distinctUsers);
console.log("wtf??");
//return [];
If I uncomment the return [] it will send the return and later it will show the should be last, so even when I dont have the return it seems to finish. It is now waiting for the response. I dont like the way my code looks so any pointer of how to make this look better I will take it.
It looks like the sellerCollection.distinct takes a callback as one of it's parameters, therefore, you cannot use async/await with a callback-style function, since it's not a promise.
I would suggest turning this call into a promise if you'd like to use async/await:
function findDistinct(hostId, eventId) {
return new Promise((resolve, reject) => {
sellerCollection.distinct(
'userId',
{ hostId, eventId: { "$ne": eventId } },
function (error, userIds) {
if (error) {
reject(error);
return;
}
if (!userIds || userIds.length === 0) {
resolve([]);
return;
}
resolve(userIds);
}
)
})
}
Then, you can use this new function with async/await like such:
async function getDistinctUsers() {
try {
const hostId = ...
const eventId = ...
const distinctUsers = await findDistinct(hostId, eventId)
if (distinctUsers.length === 0) {
return
}
const filter = {
where: {
id: { inq: userIds }
}
}
const bpUsers = await BPUser.find(filter) // assuming it's a promise
console.log(bpUsers)
} catch (error) {
// handle error
}
}
I have been wondering why adding a callback to the mongoose findOneAndUpdate function results in saving the data twice to the DB?
public async addPersonAsFavorite(userId: string, friendId: string) {
if (!await this.isPersonAlreadyFriend(userId, friendId)) {
const friendList = FriendsList.findOneAndUpdate(
{ _id: userId },
{ $push: { friendsList: friendId } },
{ upsert: true, new: true },
(err, data) => {
if (err) console.error(err);
return data;
}
);
return friendList;
}}
public async isPersonAlreadyFriend(userId: string, friendId: string) {
let isFriendFound = false;
await FriendsList.findById(userId, (err, data) => {
if (data) {
console.log(data.friendsList);
}
if (err) console.error(err);
if (data && data.friendsList.indexOf(friendId) > -1) {
isFriendFound = true;
console.log('already friend');
} else {
console.log('not friend');
isFriendFound = false;
}
})
return isFriendFound;
}
If i remove the callback, the data only gets saved once.
EDIT: added second piece of code and new question.
If someone spams the button to add friend. The friend will be added multiple times because before the first friend is added and the check can be done to prevent this it has already added the person multiple times.
How can i make sure that it completes the write to the DB before allowing the function to be called again?
Maybe the problem is in isPersonAlreadyFriend method, because you are trying to call it using async await but then you are passing a callback, what makes the method not return a promise. The rigth way to use promises with mongodb should be something like this:
public async isPersonAlreadyFriend(userId: string, friendId: string) {
let isFriendFound = false;
const data = await FriendsList.findById(userId);
if (data) {
console.log(data.friendsList);
}
if (data && data.friendsList.indexOf(friendId) > -1) {
isFriendFound = true;
console.log('already friend');
} else {
console.log('not friend');
isFriendFound = false;
}
return isFriendFound;
}
Try with this and let me know if it helps
I am working in node js express framework and I have a scenario where I have to call 2-3 nested callback functions inside for loop.
Below is my code:
for (i in jdp_tb_trades) {
var jdp_response_json_parsed = JSON.parse(jdp_tb_trades[i].jdp_response);
processConsign(jdp_tb_trades[i].tid, function(err_process_consign, lpnumber) {
if (err_process_consign) {
console.log("Some error occurred in processConsign. Error is:" + err_process_consign);
//Check if generate XML is enabled from admin end.
configuration.getOneByKey('generateXml', function(err_configuration, result_configuration) {
if (err_configuration) {
console.log('[generateXml]: generate xml enabled/disabled - No response.');
return callback(null, lpnumber);
} else {
if (result_configuration.value == 'true') {
console.log('[generateXml]: generate xml enabled.')
generateXml(jdp_tb_trades[i].tid, jdp_response_json_parsed, lpnumber, function(err_generate_xml, success_generate_xml);
if (err_generate_xml) {
return callback(err_generate_xml);
} else {
return callback(null, success_generate_xml);
}
});
} else {
console.log('[generateXml]: generate xml disabled.');
return callback(null, lpnumber);
}
}
});
} else {
//Check if generate XML is enabled.
configuration.getOneByKey(
'generateXml',
function(err_configuration, result_configuration) {
if (err_configuration) {
console.log('[generateXml]: generate xml enabled/disabled - No response.');
return callback(null, lpnumber);
} else {
if (result_configuration.value == 'true') {
console.log('[generateXml]: generate xml enabled.')
generateXml(jdp_tb_trades[i].tid, jdp_response_json_parsed, lpnumber, function(err_generate_xml, success_generate_xml) {
if (err_generate_xml) {
return callback(err_generate_xml);
} else {
return callback(null, success_generate_xml);
}
});
} else {
console.log('[generateXml]: generate xml disabled.');
return callback(null, lpnumber);
}
}
});
});
}
Update
The above code is part of a function named getOrders which is called as:
module.exports = {
getOrders: function (callback) {
getOrders(function(err_getOrders, getOrdersResponse){
if(err_getOrders){
console.log("generate Order process error:"+err_getOrders);
return callback(err_getOrders);
}
else{
console.log("generate Order process success:"+getOrdersResponse);
return callback(null, getOrdersResponse);
}
});
},
}
I have made multiple callbacks because function ends in multiple scenarios. I am not concerned about output of getOrders because I am not going to consume that output anywhere.
Here I have two functions processConsign and generateXml. generateXml is called in callback of processConsign. But I think forloop does not wait for these two tasks to get complete and keep increment the loop without waiting for processing of these two functions.
Is there any way by which I can make for loop wait for completion of these two processes and then executing the next loop?
you can use the async.each
async.each(jdp_tb_trades, (jdp_tb_trade, callback) => {
// do manipulation here
// return callback() after the process. pass err if error
}, loop_ended (err) => {
if (err) {
// Error in loop | err callback returned with err
}
// loop already ended here
});
Kindly check this
const async = require('async');
function getOrders (callback) {
async.each(jdp_tb_trades, generate_xml, (err) => {
if (err) {
// the callback return err using callback(err)
}
else {
// check the jdp_tb_trades. no error found
}
});
}
function generate_xml (jdp_tb_trade, callback) {
let jdp_response_json_parsed;
try {
jdp_response_json_parsed = JSON.parse(jdp_tb_trade.jdp_response);
} catch (err) {
return callback(err);
}
processConsign(jdp_tb_trade.tid, (err_process_consign, lpnumber) => {
if (err_process_consign) {
console.log(`Some error occurred in processConsign. Error is: ${err_process_consign}`);
//Check if generate XML is enabled from admin end.
configuration.getOneByKey('generateXml', (err_configuration, result_configuration) => {
if (err_configuration) {
console.log('[generateXml]: generate xml enabled/disabled - No response.');
// base on your callback it still a success response
// return callback(null, lpnumber);
jdp_tb_trade.lpnumber = lpnumber;
return callback();
}
else {
if (result_configuration.value == 'true') {
console.log('[generateXml]: generate xml enabled.')
generateXml(jdp_tb_trade.tid, jdp_response_json_parsed, lpnumber, (err_generate_xml, success_generate_xml) => {
if (err_generate_xml) {
jdp_tb_trade.err_generate_xml = err_generate_xml;
// return error
return callback(err_generate_xml);
} else {
jdp_tb_trade.success_generate_xml = success_generate_xml;
return callback();
// return callback(null, success_generate_xml);
}
});
}
else {
console.log('[generateXml]: generate xml disabled.');
return callback(null, lpnumber);
}
}
});
}
else {
if (result_configuration.value == 'true') {
console.log('[generateXml]: generate xml enabled.')
generateXml(jdp_tb_trades[i].tid, jdp_response_json_parsed, lpnumber, (err_generate_xml, success_generate_xml) => {
if (err_generate_xml) {
return callback(err_generate_xml);
} else {
jdp_tb_trade.success_generate_xml = success_generate_xml;
// return callback(null, success_generate_xml);
return callback();
}
});
}
else {
console.log('[generateXml]: generate xml disabled.');
jdp_tb_trade.lpnumber = lpnumber;
return callback();
// return callback(null, lpnumber);
}
}
});
}
I started to work deeply with promises, and need your help to understand why this code doesn't work as expected.
Here is what I need:
After successful profile retrieving the code should log out ('success getting Profile!') which it does, and return true as result, that should be passed to the next then() element. But instead of true, I get undefined as the result.
public verifyAuth(): angular.IPromise<boolean> {
let promise: ng.IPromise<boolean> = this._Token.getIdToken()
.then((idToken) => {
if (!idToken) {
return false;
}
else if (this._Profile.isEmpty()) {
promise = this.retrieveProfileInfo()
.then(() => {
this._$log.log('success getting Profile!');
return true;
});
} else {
return true;
}
})
.catch((error) => {
this._Token.clearToken();
this._$log.error(error);
return false;
});
return promise;
}
You are returning the first promise object before your reassign it later in the success then block. You don't need to do it just use chaining like this:
public verifyAuth(): angular.IPromise<boolean> {
let promise: ng.IPromise<boolean> = this._Token.getIdToken()
.then((idToken) => {
if (!idToken) {
return false;
}
else if (this._Profile.isEmpty()) {
return this.retrieveProfileInfo()
.then(() => {
this._$log.log('success getting Profile!');
return true;
});
} else {
return true;
}
})
.catch((error) => {
this._Token.clearToken();
this._$log.error(error);
return false;
});
return promise;
}
Note, how you simply return this.retrieveProfileInfo() which becomes new promise automatically.