This is my JS:
self.obj = {}
self.obj.accessErrors = function(data) {
var cerrorMessages = [];
for (prop in data) {
if (data.hasOwnProperty(prop)){
if (data[prop] != null && data[prop].constructor == Object) {
self.obj.fetch[accessErrors](data[prop]);
}
else {
cerrorMessages.push(data[prop]);
}
}
}
return cerrorMessages;
};
self.obj.fetch = {
X: function() {
// do stuff.
},
Y: function(callback) {
$http.get('/yposts/').then(
function(response) {
self.posts = response.data;
callback(self.posts);
},
function(response) {
self.posts = {};
self.obj.accessErrors(response.data).then(function(cerrrorMessages) {
callback(posts, cerrorMessages);
});
}
);
}
};
And I am getting an error pointing to this line:
self.obj.accessErrors(response.data).then(function(cerrrorMessages) {
The error says:
TypeError: self.obj.accessErrors(...).then is not a function
Any idea how to solve this?
self.obj.accessErrors(response.data) does not return a promise so therefore, you can't use promise methods on it.
If you want it to return a promise and you want that promise to reflect when all the fetch() operations are done and those operations are actually async, then you will have to make all your async code into using promises and you will have to combine them all using Promise.all() or the angular equivalent and convert from using callbacks in fetch to just using a promise. Right now, you have a mix which is difficult to program with.
The .then() construction is only needed when using Promise objects - essentially, instead of returning a value, the function returns an object that resolves to a value at some point in the future (which is then passed into the function that you pass to .then().
But you are right in that you need an asynchronous pattern to do this correctly, since fetch.Y is an asynchronous method. A good thing to do would be to create an array of promises at the beginning of your accessErrors function, like so:
var fetchPromises = [];
and then replace self.obj.fetch[accessErrors](data[prop]); with something that calls push on that array to add the promise that fetch returns to it.
Then, instead of returning accessErrors, return Promise.all(fetchPromises).
This will require some fairly significant modification to your code, however - namely, you will need to rewrite it so that it uses the Promise API instead of this callback by itself (which shouldn't be too difficult to do).
Related
I'm writing a test using Selenium and JavaScript. I'm new to both, and also new to functional programming and promises. I'm trying to create a function that needs to do 3 things:
Click on an input
Clear the input
SendKeys to input
My current function does not work:
var clearAndSendKeys = function(driver, elementIdentifier, sendKeys) {
var returnValue;
driver.findElement(elementIdentifier).then(function(inputField){
inputField.click().then(function() {
inputField.clear().then(function() {
returnValue = inputField.sendKeys(sendKeys);
});
});
});
return returnValue;
}
The function would then be called as for example:
clearAndSendKeys(driver, webdriver.By.id('date_field'), '14.09.2015').then(function(){
//Do stuff
});
I expected the variable returnValue to contain the promise from sendKeys. However the function clearAndSendKeys returns the undefined variable before sendKeys is ran. I assume this is because returnValue was never defined as a promise, and so the program does not know that it needs to wait for sendKeys.
How can I make my function clearAndSendKeys return the promise from sendKeys? I'd rather avoid having to add a callback to the clearAndSendKeys function.
Edit: Removed .then({return data}) from the code as this was a typo.
You have to return each promise from the .then callback:
var clearAndSendKeys = function(driver, elementIdentifier, sendKeys) {
return driver.findElement(elementIdentifier).then(function(inputField){
return inputField.click().then(function() {
return inputField.clear().then(function() {
return inputField.sendKeys(sendKeys);
});
});
});
}
The promise returned by .then will resolve to the same value as the value returned from the callback.
See Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference for why your current code does not work. Promises are asynchronous.
First of all its probably not the best idea to nest promises, completely defeating their main purpose of eliminating callback hell. then callback can return Thenable object that allows to create nice chains of async operations.
In this case you just need to store reference to input field available as a result of the first async operation in the scope of the main function and then create chain of async operations that can be returned from this function.
var clearAndSendKeys = function(driver, elementIdentifier, sendKeys) {
var inputFieldRef;
return driver.findElement(elementIdentifier)
.then(function(inputField){
inputFieldRef = inputField;
return inputField.click();
}).then(function() {
return inputFieldRef.clear();
}).then(function() {
return inputFieldRef.sendKeys(sendKeys);
});
}
(function() {
"use strict";
var storage = chrome.storage.sync;
var localStorage = null;
function getOutsideScope(property) {
if (localStorage.hasOwnProperty(property)) {
return localStorage[property];
}
}
function fulfill(data) {
return data;
}
function rejected(err) {
log(err);
}
window.app.storage = {
get: function(storageKey, storageProp) {
var promise = new Promise(function(fullfill, reject) {
chrome.storage.sync.get(storageKey, function(data) {
if (data.hasOwnProperty(storageKey)) {
fullfill(data[storageKey]);
} else {
reject(new Error(storageKey + " does not exist in the storage values."));
}
});
});
return promise.then(fulfill, rejected);
},
set: function(storageKey, storageItem) {
},
onChanged: function(fn) {
}
};
})();
So the above is my IIFE wrapper for chrome storage, and well the return is being a pain in the bleep. So I decided to give Promises a try, this is my first time so don't be too rough on me on this. Basically this is what I want to do
var property = app.storage.get("properties");
//property should equal "value"
//except it returns undefined
So adding the promises it does get the value except it returns this
Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: "value"}
Am I doing something wrong with Promises I've tried reading HTML5Rocks, the MDN, and video tutorials except it doesn't really mention much of how to return a value. This does NOT work
get:function(storageKey,storageProp) {
chrome.storage.sync.get(storageKey,function(data) {
//for simplicity and no error checking -_-
return data[storageKey];
});
}
The function returns exactly what it is supposed to return: A promise. To get the value once the promise is fulfilled, you add a callback via .then:
app.storage.get("properties").then(function(property) {
// property will be "value"
});
From MDN:
A Promise represents a proxy for a value not necessarily known when the promise is created. It allows you to associate handlers to an asynchronous action's eventual success value or failure reason. This lets asynchronous methods return values like synchronous methods: instead of the final value, the asynchronous method returns a promise of having a value at some point in the future.
[...]
A pending promise can become either fulfilled with a value, or rejected with a reason (error). When either of these happens, the associated handlers queued up by a promise's then method are called.
I'm attempting to run a series of functions based upon the Q API using their first strategy for sequences. This suggests the pattern:
var funcs = [foo, bar, baz, qux];
var result = Q(initialVal);
funcs.forEach(function (f) {
result = result.then(f);
});
return result;
What structure are each of the functions within the array meant to take? I am quite confused about when to use the return def.promise;. Is that simply always the last line? Will it frequently or always immediately follow def.resolve(someVar). Is something like this then ended structure?
function foo(f){
var def = Q.defer();
f++;
def.resolve(f);
return def.promise;
}
So that each subsequent function within the array will receive the newly calculated value of f: in this case, if var initialVal = 1; and four functions each incrementing f++, the returned result will be 4? How do I access that returned value? console.log(result) prints { state: 'pending' } .
What structure are each of the functions within the array meant to take?
Q.js allows promises to be created in several ways. For example :
function foo(value) {
var def = Q.defer();
def.resolve(value + 1);
return def.promise;
}
function foo(value) {
return Q(value + 1);
}
function foo(value) {
return Q.Promise(function(resolve, reject) {
resolve(value + 1);
});
}
Other Promise libs are similar, but not necessarily so flexible. Native js Promises must be constructed with the third of these approaches.
However, in the real world you will only rarely need to create your own Promise. You will typically be dealing with promise-returning lib methods that someone else has written. For example :
function foo(value) {
return lib.doSomethingAsync(value, and, other, params);
}
How do I access that returned value?
The code is easier to understand if member name "result" is replaced with "promise", and result.then(f) is rewritten with an anonymous function that calls f().
function performAsyncSequence() {
var promise = Q(initialVal);
funcs.forEach(function (f) {
promise = promise.then(function(previousResult) {
return f(previousResult);
});
});
return promise;
}
This is 100% equivalent to the code in the question, but now it should be clearer how the previous result is passed down the promise chain.
Accessing all previous promise results in the sequence is more complicated. The answers here discuss the subject comprehensively.
I'm still learning about Promises and I'm facing a problem. Maybe you can help me.
I have a task which does some checks before running the real stuff. Some of those checks are sync, others are async.
So I'd like to have something like this:
q.all([
jenkins.checkConfig,
gitlab.checkConfig,
sonar.checkConfig
])
.then(
function() {
doSomethingReallyCoolHere();
}
);
But what if sonar.checkConfig is not a promise ? How can I give it a promise behavior ?
Currently I'm doing this
var checkConfig = function() {
var qChecked = q.defer();
var isOK = awesomeSyncTestHere();
if (isOK) {
qChecked.resolve();
}
else {
qChecked.reject();
}
return qChecked.promise;
}
But it looks stupid.
I guess that's not the good way to do it, right ?
Thanks a lot
There is no problem with placing non-promise values in the input array to Q.all. They will just be treated like a promise that had been fulfilled with them.
However, you need to put actual values (or promises) in the array, not the functions that would return them. Just go for
Q.all([
jenkins.checkConfig(),
gitlab.checkConfig(),
sonar.checkConfig()
]).then(doSomethingReallyCoolHere);
If you do want your checks to throw (and prevent the then callback from running), you will need to return a rejected promise indeed. You shouldn't use a deferred for that, though:
function checkConfig() {
if (awesomeSyncTestHere())
return Q(true);
else
return Q.reject(new Error("awesome fail"));
}
or, if you have a test that really throws, just use Q.try
function checkConfig() {
return Q.try(awesomeSyncTestHere);
}
I have the following function defined in a service, in which I use $http to get some data and set some of the fields of an object:
function getComplexObject () {
var complexObject = {
'A': null,
'B': null
}
$http.get('someURI').then(function(data) {
complexObject.A = data;
//Do some processing and set B
return complexObject;
})
};
So until now I've used a promise with the $http service but I don't know how to wrap the function with $q so that I can ultimately use it in the following manner:
//Using my function as a promise
myService.getComplexObject().then(function (data) {
this.complexObject = data;
})
I've searched for tutorials on angular promises but most of them simply explain what a promise is or do something similar to what I already did, I'm guessing what I want to do is possible but I can't find the right syntax or the right tutorial.
Just put return in front of the $http.get(...).then(...) chain, i.e.
return $http.get(...).then(...);
This will ensure that the result of getComplexObject is a promise that will (when its resolved) contain the value of complexObject.
That is exactly what .then is designed for - it takes the promise from $http.get() and returns a new promise that will ultimately be resolved with its own return value instead of the original one.
FWIW, I'd put the declaration of complexObject inside the .then call - there's no need for it to be in the outer scope and it'll inadvertently cause a closure over that variable.
NB: Also, be careful about using this in your final callback - it may not be what you think it is!
You could return a promise and resolve if later (see: https://docs.angularjs.org/api/ng/service/$q).
function getComplexObject () {
var deferred = $q.defer();
var complexObject = {
'A': null,
'B': null
}
$http.get('someURI').then(function(data) {
complexObject.A = data;
//Do some processing and set B
deferred.resolve(complexObject);
});
return deferred.promise;
};