I have a factory object that contain private object, which is used to cache result retrieved from the api using the factory available functions.
global.mainApp.factory('SessionFactory', function (UserEndpointsResource, SiteEndpointsResource) {
var data = {
/**
* #type {boolean|null}
*/
isLoggedIn: null
};
return {
isUserLoggedIn: function (callback) {
if (data.isLoggedIn != null) {
callback(data.isLoggedIn);
}
else {
UserEndpointsResource.isLoggedIn().$promise.then(function (res) {
var isUserLoggedIn = res.status == 1;
// Trying to set the result to the outer scope data variable
data.isLoggedIn = isUserLoggedIn;
callback(isUserLoggedIn);
}, function (failureData) {
data.isLoggedIn = false;
callback(false);
});
}
},
...
};
});
The problem that every time I call the isUserLoggedIn function, data.isLoggedIn is always null.
How can I alter the factory data object inside the promise then function?
Thanks.
Using the suggestion supplied in the comments, aka do not store promise results, store promises themselves, this is the modified working code!
global.mainApp.factory('SessionFactory', function (UserEndpointsResource, SiteEndpointsResource) {
var data = {
/**
* #type {boolean|null}
*/
isLoggedIn: null
};
return {
isUserLoggedIn: function (callback) {
if (data.isLoggedIn != null) {
data.isLoggedIn.then(function (isLoggedIn) {
callback(isLoggedIn.status == 1);
});
}
else {
data.isLoggedIn = UserEndpointsResource.isLoggedIn().$promise.then(function (res) {
var isUserLoggedIn = res.status == 1;
callback(isUserLoggedIn);
return isUserLoggedIn;
}, function (failureData) {
data.isLoggedIn = false;
callback(false);
return null;
});
}
}
};
});
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);
});
Hello how can I write the following code so that there is no race condition if I return inside the get() function it only returns from that function but if I return from the outer function it prematurely returns.
function checkifvalid (userIdPassed) {
// code to be executed
var params43 = {
TableName: 'users',
Key: {
'pid': req.user.iden
}
}
var returnVal = null
docClient.get(params43, function (err43, data43) {
if (err43) {
return res.json({'errasdsd342sd': 'erhf32623hrf'})
} else {
if (data43.Item.useract && data43.Item.curadmin != '0') {
returnVal = true
} else {
returnVal = false
}
}})
if (returnVal !== null) {
return returnVal
}
}
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;
}
}
I have a service designed which takes some parameter and then loops through input array, and find out which item is empty and then add those to and array and then after $q.all resolved it should give me the array of empty items.
input is array of items
function getElements(inputs) {
var elements= [],
promise, whenPromise,
promises = [],
mainPromise = $q.defer();
if (inputs.length === 0) {
mainPromise.resolve(elements);
return mainPromise.promise;
}
angular.forEach(inputs, function (input) {
promise = getPromises(input);
whenPromise = $q.resolve(promise).then(function (response) {
$timeout(function() {
if (response.isEmpty) {
**//perform action with the response.data;**
}
});
}, function () {
});
promises.push(whenPromise);
});
$q.all(promises).finally(function () {
mainPromise.resolve(outdatedEntities);
});
return mainPromise.promise;
}
function getPromises(input) {
var deferred = $q.defer();
someSerivce.getItemDetails(input.Id).then(function (value) {
if (value === null) {
$timeout(function () {
deferred.resolve({data: input, isEmpty: true});
});
} else {
//have item locally, but need to see if it's current
input.isEmpty().then(function (isEmpty) {
$timeout(function () {
deferred.resolve({data: input, isEmpty: isEmpty});
});
}, function (error) {
deferred.reject(error);
});
}
});
return deferred.promise;
}
Now, the problem is the $q.all is never resolved. Even though all the internal promises are getting resolved.
This should work:
function getElements(inputs) {
var elements = [],
promise, whenPromise,
promises = [],
mainPromise = $q.defer();
if (inputs.length === 0) {
mainPromise.resolve(elements);
return mainPromise.promise;
}
angular.forEach(inputs, function (input) {
promise = getPromises(input);
whenPromise = promise.then(function (response) {
if (response.isEmpty) {
* *//perform action with the response.data;**
}
}, function () {
});
promises.push(whenPromise);
});
return $q.all(promises).finally(function () {
return outdatedEntities;
});
}
function getPromises(input) {
return someSerivce.getItemDetails(input.Id).then(function (value) {
if (value === null) {
return {data: input, isEmpty: true};
} else {
//have item locally, but need to see if it's current
return input.isEmpty().then(function (isEmpty) {
return {data: input, isEmpty: isEmpty};
}, function (error) {
return error;
});
}
});
}
I have this function:
$scope.PinTicketSearch = function(pinTicket) {
if (pinTicket != null) {
ticketService.searchTicket(pinTicket)
.then(function(response) {
$location.search({
"ticketPin": pinTicket
});
$scope.TicketDetail = response;
$scope.ShowDetailsAboutTicket = true;
});
}
}
and i have this part of code:
if ($location.search().ticketPin)
{
}
How can i call this function $scope.PinTicketSearch and pass parameters from $location.search().ticketPin. I tried with $scope.PinTicketSearch($location.search().ticketPin) but i get an error
PinTicketSearch is not a function
You should to use a callback, instead of your if:
$scope.PinTicketSearch = function(pinTicket, success) {
if(pinTicket != null) {
ticketService.searchTicket(pinTicket)
.then(function(response) {
$location.search({
"ticketPin": pinTicket
});
$scope.TicketDetail = response;
$scope.ShowDetailsAboutTicket = true;
success();
});
}
}
and your "if", will be:
$scope.PinTicketSearch(pinTicket, function() {
// your original "if" body
});