Javascript promise conversion - javascript

I'm having a hard time understanding how promises works. I've seen some examples and some of it makes sense. However, when I try to actually use it I get stuck. I have the following example:
(I'm using q and nodejs. getItem() returns a promise, but my function doesn't wait for it.)
function calculate(id, item) {
var calcId = id ? id : getItem(item);
if (!id && calcId) { calcId = calcId.Id; }
if (calcId) {
update(calcId);
} else {
insert(item);
}
}
Based on the examples, I don't see how to do it without code duplication.

Promises are resolved asynchronously, so you can't treat them as synchronous as in your example. What you want to do is coerce the value is a Q promise if needed, then proceed from there, that way you have deterministic behavior and no code duplication.
I'm making some assumptions here about update and insert returning promises and returning them when needed so that calculate itself returns a promise.
function calculate( id, item ) {
return Q( id || getItem(item) ).then(function( calcId ) {
// Code seems weird to be, but it's based on the original example.
// Might want to review this.
if ( !id && calcId ) {
calcId = calcId.Id;
}
return calcId ?
update( calcId ) :
insert( item );
});
}

Don’t duplicate your id checks:
function calculate(id, item) {
var calcId;
if (id) {
calcId = id;
} else {
calcId = getItem(item).Id;
}
if (calcId) {
update(calcId);
} else {
insert(item);
}
}
Now make calcId consistently a promise holding an Id:
function calculate(id, item) {
var p;
if (id) {
p = Promise.resolve({ Id: id });
} else {
p = getItem(item);
}
return p.then(function (calcId) {
if (calcId.Id) {
return update(calcId.Id);
} else {
return insert(item);
}
});
}
Where, in the case of Q, Promise.resolve is Q.resolve, or just Q.
Bonus: as a generator!
function calculate(id, item) {
var calcId = id ? id : yield getItem(item);
if (!id && calcId) { calcId = calcId.Id; }
if (calcId) {
update(calcId);
} else {
insert(item);
}
}

Several points :
Promise-wrapped results need to be handled with a function specified as a parameter of a promise method (eg .then(fn))
Q(x) can be used to ensure that x is Promise-wrapped. The operation is transparent if x is already a promise - it won't be double wrapped
you need safety in case both id and item are empty or missing
you need further safety in case calcId is falsy and item was not provided
id versus calcId.Id can be more elegantly catered for.
function calculate(id, item) {
if(!id && !item) {
throw new Error("'calculate(): parameters empty or missing'");
}
return Q(id ? {Id:id} : getItem(item)).then(function(resultObj) {
var calcId = resultObj.Id;
if(calcId) {
update(calcId);
} else {
if(item) {
insert(item);
} else {
throw new Error("'calculate(): `calcId` is falsy and `item` is empty of missing'");
}
}
return calcId; //in case you need to chain calculate(...).then(...)
//(Alternatively, if update() and insert() return promises, then return them instead as per dherman's answer)
});
}
Call as follows :
calculate(myId, myItem).then(function(calcId) {
//(optional) anything else you wish to do with `calcId`
}).catch(function(e) {
console.error(e);
});

Related

How to return multiple functions dynamically Javascript

I apologize if I didn't ask my question in the correct way. Let me explain my problem.
I'm working on a search function in a table, I'm adding the option to search in specific columns.
Right now I'm manually checking if the checkboxes are selected or not, then return the proper search function.
However I want to make it dynamically, I don't want to edit this code after every time I add new columns to the table. That's where I'm struggling.
This is my current code:
vm.$watch('searchTerm', function (searchTerm) {
if (!searchTerm) {
vm.filteredTable = angular.copy(vm.table);
} else {
searchTerm = searchTerm.toLowerCase();
return vm.filteredTable.rows = vm.table.rows.filter(function (row) {
if (vm.searchFilter[0] === true ){
return (contains(row[vm.table.columns[0].value], searchTerm))
}
if (vm.searchFilter[1] === true ){
return (contains(row[vm.table.columns[1].value], searchTerm))
}
if (vm.searchFilter[2] === true ){
return (contains(row[vm.table.columns[2].value], searchTerm))
}
if (vm.searchFilter[3] === true ){
return (contains(row[vm.table.columns[3].value], searchTerm))
}
if (vm.searchFilter[4] === true ){
return (contains(row[vm.table.columns[4].value], searchTerm))
}
if (vm.searchFilter[5] === true ){
return (contains(row[vm.table.columns[5].value], searchTerm))
}
if (vm.searchFilter[6] === true ){
return (contains(row[vm.table.columns[6].value], searchTerm))
}
if (vm.searchFilter[7] === true ){
return (contains(row[vm.table.columns[7].value], searchTerm))
}
});
}
});
This is the way I created it, I tried to use a loop, but I can't return the functions if there is a loop. Also adding return before the for loop wont help either.
vm.$watch('searchTerm', function (searchTerm) {
if (!searchTerm) {
vm.filteredTable = angular.copy(vm.table);
} else {
searchTerm = searchTerm.toLowerCase();
return vm.filteredTable.rows = vm.table.rows.filter(function (row) {
for (let i=0; i<vm.searchFilter.length; i++){
if (vm.searchFilter[i] === true ){
return (contains(row[vm.table.columns[i].value], searchTerm))
}
}
});
}
});
This is probably a very easy case for the most of you, so I apologize if I'm just being stupid right now. I'm working since 2 hours on this till now...
EDIT:
The function I mean:
const contains = (value, searchTerm) => {
if (!value) return false;
return value.toLowerCase().includes(searchTerm);
}
2nd EDIT:
I just realized after a kind member told me that, that the first version isnt working either the way I want it.
There is the option to have a multiple selection, so if I select the first two checkboxes, then it should search in BOTH of them and not only one.
You can just iterate over the whole thing like this:
vm.$watch('searchTerm', function (searchTerm) {
if (!searchTerm) {
vm.filteredTable = angular.copy(vm.table);
} else {
searchTerm = searchTerm.toLowerCase();
return vm.filteredTable.rows = vm.table.rows.filter(row =>
vm.searchFilter.some((filter, index) => filter && contains(row[vm.table.columns[index].value])))
}
By using Array.prototype.some you essentially ask whether your row matches at least one of the filters.

Unexpected results from a function

I have the following two blocks of code that I am trying to debug.
function getSectionId(target){
let element = target;
if(element.hasAttribute('id')){
console.log(element.id);
return element.id;
}
else {
getSectionId(element.parentElement);
}
};
function coverageLimitHandler(event) {
const target = event.target;
if (target.getAttribute('data-status') !== 'set') {
let itemBlock = addLineItem();
let sectionId = getSectionId(target);
let attribute = '';
console.log(sectionId);
}
}
The event fires and the functions run, but the above gives the unexpected following results
//first-coverage-section (this one is expected.)
//undefined (this is expected to be the same, but is not.)
And I cannot for the life of me figure out why this is happening.
the problem is that your recursive call is not returning anything.
when you do:
getSectionId(element.parentElement);
it will call the function and maybe some day, the if above
if(element.hasAttribute('id')){
console.log(element.id);
return element.id;
}
will return something, but that won't be returned to the previous calls therefore your main call wont have anything to return, so to solve this you need to do this:
function getSectionId(target){
let element = target;
if(element.hasAttribute('id')){
console.log(element.id);
return element.id;
}
else {
// add this return and your function will work correctly.
return getSectionId(element.parentElement);
}
};
basically you have something like this:
function recursiveNotWorking(n) {
if (n === 5) {
return "something"
} else {
// you dont return anything, then something never bubbles up
recursiveNotWorking(n + 1);
}
}
function recursiveWorking(n) {
if (n === 5) {
return "something"
} else {
// we return something
return recursiveWorking(n + 1);
}
}
console.log("NW: ", recursiveNotWorking(1));
console.log("Working: ", recursiveWorking(1));
You need to return the result of the recursive call:
const getSectionId = target => {
if (target.hasAttribute('id') {
return target.id;
}
// You should also check that parentElement exist
// Otherwise you might reach the root node and then parentElement could become null
return getSectionId(target.parentElement);
};
Alos, this can be re-written as well as one liner:
const getSectionId = t => t.id || getSectionId(t.parentElement)
You don't have return in the first function and you don't check on undefined. Also you don't need to use the element variable. It's useless.
Maybe this will work:
function getSectionId(target){
if (typeof target === 'undefined') return null;
if(target.hasAttribute('id')){
console.log(target.id);
return target.id;
}
return getSectionId(target.parentElement);
}

creating a service in angularJS, using restangular promises

Ok, this is driving me mad, basically what I am trying to do is create a service to get and evaluate user capabilities, I am using WP REST API. I use restangular to get my JSON data.
At this stage I am testing the function in the controller itself, but no matter where I test it, be it in my custom service using this.method or inside the controller, with or without using $scope the result is always undefined. I know I am missing something either in the way I am returning true or false inside the function, or there is something fundamentally different when it comes to promises in JS. Here is the code:
var current_user = parseInt(o2_i18n.user_id),
currentUserCapabilities,
capability;
$scope.currentUserCan = function(capability) {
if(current_user !== '0') {
wpAPIResource.one('users').get()
.then(function(allUsers){
for (var i = 0; i < allUsers.length; i++) {
if ( allUsers[i].id === current_user ) {
var currentUserCapabilities = allUsers[i].capabilities;
for(var prop in currentUserCapabilities){
if (capability === prop) {
//$log.log( prop );
return prop;
} else {
//$log.log( prop );
return false;
}
}
}
}
}, function(reason){
$log.error(reason);
});
} else {
//The user is not logged in, therefor no capabilities
return false;
}
};
$log.log($scope.currentUserCan('publish_posts'));
if ( $scope.currentUserCan('publish_posts') ) {
$log.log( 'Yes I Can!' );
} else {
$log.warn('No Can\'t Do!');
}
Your currentUserCan function doesn't return anything if current_user !== '0'. You should have it return a promise, for example (for the following you'll need to inject the $q service)
$scope.currentUserCan = function(capability) {
if(current_user !== '0') {
// note the "return" here
return wpAPIResource.one('users').get().then(function(allUsers){
for (var i = 0; i < allUsers.length; i++) {
if ( allUsers[i].id === current_user ) {
var currentUserCapabilities = allUsers[i].capabilities;
for(var prop in currentUserCapabilities){
if (capability === prop) {
return prop;
}
}
}
}
return false;
}, function(reason){
$log.error(reason);
return $q.reject(reason); // you still want the promise to fail
});
} else {
return $q.resolve(false);
// this turns the static value into a promise so the API for this
// function is consistent
}
};
then consume the function like this
$scope.currentUserCan('publish_posts').then(function(can) {
if (can) {
$log.log('Yes I Can!');
} else {
$log.warn("No Can't Do!");
}
});
I also cleaned up your loop a little. In your OP, there was no point in the inner loop and you didn't have a return value if the user was not found in the allUsers array.

Function with forEach returns undefined even with return statement

I'm just making a function for checking a value of something in my object array, but for some reason it keeps returning undefined. Why is that?
Demo: http://jsfiddle.net/cNYwz/1/
var data = [{
"Key": "1111-1111-1111",
"Email": "test#test.com"
}, {
"Key": "2222-2222-2222",
"Email": "test#boo.com"
}];
function getByKey(key) {
data.forEach(function (i, val) {
if (data[val].Key === key) {
return data[val].Key;
} else {
return "Couldn't find";
}
});
}
var asd = getByKey('1111-1111-1111');
console.log(asd);
In your function, you're returning from the function passed to forEach, not from getByKey.
You could adapt it like this :
function getByKey(key) {
var found = null;
data.forEach(function (val) {
if (val.Key === key) {
found = val;
}
});
return found;
}
But this would iterate over all elements, even if the item is immediately found. That's why you'd better use a simple for loop :
function getByKey(key) {
for (var i=0; i<data.length; i++) {
if (data[i].Key === key) {
return data[i];
}
}
}
Note that I also adapted your code to return the value, not the key. I suppose that was the intent. You might also have been confused with another iteration function : the first argument passed to the callback you give to forEach is the element of the array.
Your function getByKey has no return statement. The two returns are for the anonymous function used by forEach.
You're not returning anything to the outer scope, try this alternative:
function getByKey(key) {
var result = data.filter(function (i, val) {
return data[val].Key == key;
});
return result.length ? result : 'Not found';
}
Try storing the positive result as a variable, and then returning that variable (or a "Couldn't find" in case nothing is written) at the end of the function after the forEach loop.
function getByKey(key) {
var result;
data.forEach(function (val, i) {
if (data[val].Key === key) {
result = data[val].Key;
}
});
return result || "Couldn't find";
}
In addition to the ideas in the other answers, you're better off using Array.prototype.some, rather than forEach. That will let you stop when you find the first match:
function getByKey(key) {
var found = null;
data.some(function (val) {
if (val.Key === key) {
found = val;
return true; //stop iterating
}
});
return found;
}
You might also consider using filter, which can return an array containing just the objects where key matches:
function filter_array_by_key(key){
return data.filter(function(v){
return v.Key===key;
};
}
To get the first matching object, you can then use filter_array_by_key(key)[0], which will yield undefined if there was no match.

How to accomplish this without using eval

Sorry for the title but I don't know how to explain it.
The function takes an URI, eg: /foo/bar/1293. The object will, in case it exists, be stored in an object looking like {foo: { bar: { 1293: 'content...' }}}. The function iterates through the directories in the URI and checks that the path isn't undefined and meanwhile builds up a string with the code that later on gets called using eval(). The string containing the code will look something like delete memory["foo"]["bar"]["1293"]
Is there any other way I can accomplish this? Maybe store the saved content in something other than
an ordinary object?
remove : function(uri) {
if(uri == '/') {
this.flush();
return true;
}
else {
var parts = trimSlashes(uri).split('/'),
memRef = memory,
found = true,
evalCode = 'delete memory';
parts.forEach(function(dir, i) {
if( memRef[dir] !== undefined ) {
memRef = memRef[dir];
evalCode += '["'+dir+'"]';
}
else {
found = false;
return false;
}
if(i == (parts.length - 1)) {
try {
eval( evalCode );
} catch(e) {
console.log(e);
found = false;
}
}
});
return found;
}
}
No need for eval here. Just drill down like you are and delete the property at the end:
parts.forEach(function(dir, i) {
if( memRef[dir] !== undefined ) {
if(i == (parts.length - 1)) {
// delete it on the last iteration
delete memRef[dir];
} else {
// drill down
memRef = memRef[dir];
}
} else {
found = false;
return false;
}
});
You just need a helper function which takes a Array and a object and does:
function delete_helper(obj, path) {
for(var i = 0, l=path.length-1; i<l; i++) {
obj = obj[path[i]];
}
delete obj[path.length-1];
}
and instead of building up a code string, append the names to a Array and then call this instead of the eval. This code assumes that the checks to whether the path exists have already been done as they would be in that usage.

Categories

Resources