I'm using AngularJS application. I have a form. On submit I'm calling a function. I have used the javascript try/catch/finally block
$scope.save = function() {
try {
//Block of code to try
$scope.submit.text = "Submitting";
$scope.submit.disable = true;
$timeout(function(){
alert('successfully saved');
}, 5000);
}
catch(err) {
//Block of code to handle errors
}
finally {
alert("finally");
$scope.submit.text = "Submit";
$scope.submit.disable = false;
}
}
I used a timer for now. But later I may use AJAX call. the problem is
finally block gets executed before the time finishes. How to solve this?
The reason why the finally method gets executed before the time is finished is that javascript methods are not running asynchronous. Promises will solve this problem.
Here is how promise is defined in q.js which is used in angular:
A promise is an object that represents the return value or the thrown
exception that the function may eventually provide. A promise can also
be used as a proxy for a remote object to overcome latency.
One of the guarantees promises make is that the either the success or the error callback will be invoked, but never both. What happens if you need to ensure a specific function executes regardless of the result of the promise? You can do this by registering that function on the promise using the finally() method.
Suppose you have a function getData() where you are making some $http request and obtain some data from backend. Then you can use:
var promise = getData()
.then(function(data) {
console.log(data)
}, function(error) {
console.error(error)
})
.finally(function() {
console.log()
})
})
Related
Due to asynchronous Ajax call we are using promise, So that other JS waiting for Ajax success object can access its updated property.
We've 300 JS which will call this promise, Do we've any constraint if so many JS will call promise, it might start pending or never resolved.
Please advise
Thanks in advance
(function () {
window.myObject = window.myObject || {};
let isProfileUpdated = false;
myObject.waitforProfile = new Promise(function (resolve, reject) {
let maxTry = 50;
let currentTry = 0;
let tryForProfile = function () {
if (currentTry < maxTry && isProfileUpdated) {
resolve();
} else if (currentTry < maxTry) {
currentTry++;
setTimeout(tryForProfile, 350);
}
else {
reject('profile never resolved');
}
}
tryForProfile();
});
$.ajax({
url: 'www.google.com',
async: true,
success: function (result) {
result = result.model;
if (result != undefined) {
window.myObject.title = result.title
isProfileUpdated = true;
}
}
});
})();
Caller - We have so many callers dependent on MyObject properties
function initalizeNavigation() {
if (myObject.title !== "") {
createAuthenicatedUtilityNav();
} else {
createSignedOutUtilityNav();
}
}
myObject.waitforProfile(initalizeNavigation)
.catch(function (message) {
console.log(message)
});
There several errors in the algorithm the code was written to follow. Program design needs to fixed before throwing out the code and replacing it. One design approach may be to
Create a global Promise object to be fulfilled with the user profile requested from the server in the ajax call is received at the front end. Call it, say, profilePromise.
Issue an ajax call to obtain the user profile. If it succeeds, resolve profilePromisewith the decoded profile object. If it fails, rejectprofilePromise` with the notified error or error message.
Add a catch handler to profilePromise to inform the user to try again later (or whatever) if it becomes rejected. The posted code doesn't retry the server call so I haven't tried to include it here.
Set up a timeout to reject profilePromise if too much time has elapsed. If the timer calls back attempts to reject the promise after the ajax call has succeeded and fulfilled the promise, rejecting the promise is simply ignored - settled promise state and values are immutable.
The 300+ code points throughout the code can await profilePromise to get the user profile or, alternatively, call profilePromise.then with a fulfillment handler to be called with the profile object after it arrives.
The code points must process profile data in an async function by using syntax similar to
const profile = await profilePromise;
or add a fulfillment handler to process the data using syntax similar to
profilePromise.then( profile => { do specific thing with profile data});
What you cannot do is "call" the promise as if it were a function to get the profile, or make a synchronous function that magically returns profile data before it arrives back from the server.
Put error checking in the .catch handler added in step 3. There is no need to recheck throughout the code - the fulfillment handlers will never get called.
A totally different approach might be tp ensure critical parts of application code don't execute before the profile data has been obtained and placed in a global variable.
In a single page app this might involve modal overlays or hidden sections of HTML that prevent user interaction with the code before being allowed to do so.
Please research the use of async functions and the await operator, or adding fulfillment handlers to a promise using its then method if unfamiliar with them.
I am currently learning javascript and i find this often on tuitorials i watch. Javascript automatically pass the result of a function as a parameter to the next function.
fetch('SampleApi')
.then( function(data) { return data.json() } )
.then( function(data) { console.log(data.name); } )
.catch(function(err) { console.log(error) })
the result of the fetch goes directly as a parameter to the function then the result of the function goes directly as a parameter to the next function.
I just want to know what is this and what is this called. I need to understand this before diving deep.
EDIT: modified example to show passing parameters between methods to directly answer the qeustion.
The underlying premise is that JavaScript can act upon a returned result immediately as long as the operation requested is sane and available. A simple example is shown here:
console.log("|" + " This is a string with extra spaces ".trim() + "|");
This can be accomplished in your own code as well. In the following example, note the return of the operation is the object itself. This allows us to carry out any operation available within that object upon the return from some other operation in the same object:
class Test {
sayHi(message = "Passed Message") {
console.log('parameter: ' + message);
this.message = message;
return this;
}
sayBye(cb) {
cb(this.message);
return this;
}
}
const test = new Test();
test.sayHi("hello!").sayBye(passed => console.log('passed in: ' + passed));
So throughout your experience with JavaScript you will find many instances of this idea that is called method chaining.
In the example you provided, it is simply this concept applied to the fetch API.
It's called function chaining.
The fetch function returns an object that has a function then, which accepts a function as its parameter.
When then is called, it executes the given function with the data stored within the object as a parameter, and stores the return value as the new internal data, which is passed to the function in the second then call. then returns the same object that was returned from fetch.
The call to catch will check if an error was encountered (and stored into the object), and if so, will call the given function with the error.
You could spread out the whole thing, without chaining:
var o = fetch('SampleApi');
o = o.then(function(data) { return data.json(); });
o = o.then(function(data) { console.log(data.name); });
o.catch(function(error) { console.log(error); });
The short answer is "That's just how promises work"
To better illustrate WHY this is good behavior:
function getOrganizedData() {
return fetch("somedata").then(function(rawData) {
// do some stuff
return organizedData;
});
}
Anyone using this function will be able to use the organizedData - so if you have a shared service, only that service needs to know how to translate rawData into organizedData for the rest of the application to use.
What if that's not the behavior you want?
Easy, just don't chain the promises:
const fetchPromise = fetch('SampleApi');
fetchPromise.then( function(data) { return data.json() } );
fetchPromise.then( function(data) { console.log(data.name); } );
fetchPromise.catch(function(err) { console.log(error) });
But wait, won't I get errors with that approach because the catch isn't applied to each chain?
Yes! So, you need to catch BOTH chains:
const errorFunction = function(err) { console.log(error) };
const fetchPromise = fetch('SampleApi');
fetchPromise.then( function(data) { return data.json() } ).catch(errorFunction);
fetchPromise.then( function(data) { console.log(data.name); } ).catch(errorFunction);
NOTE: This chaining behavior applies to .catch as well:
fetch("somethingThatWillError")
.then(function() { console.log("THEN - I will not run because there's an error"); })
.catch(function() { console.log("CATCH - I will run because I am handling the error"); })
.then(function() { console.log("THEN - I will run because the error has been handled"); })
.catch(function() { console.log("CATCH - I will not run because there is no error to handle"); })
Edit : For resume, the result of fetch is the first data in case of success, the second data is the return of the first then and err the result of fetch in case of reject.
You are actually playing with promise here, you can look some documentation about Promise here.
In javascript your running task can be synchronous or asynchronous, promise is for handling asynchronous task. You can know more about asynchronous here.
To be quick and simple a promise can be resolve now, in ten minutes or never. When you are building a Promise, you need an executor which is passed with two arguments, a resolve function and a reject function, they will be call just before the final resolution of your promise, (after an API call for exemple).
The reject gonna be call if your promise fail (timed out on your api call for example) and the resolve gonna be call on the success of your promise. In this two callbacks (resolve and promise), if you don't know about callback you have to learn it here, you will have an argument with the return of the resolution of your promise, for example if the promise is rejected you gonna have the reason in a message and in the resolve you gonna have the data that you need from your api call.
In your case, the fetch is build like fetch = new Promise(...); where then(...) is the success resolver and catch(...) the rejected resolver. You can have a finally(...) which is called after the then() and catch().
And the data in your example is the result of your promise if it's a success and err is the result in case of error result.
I would like to test this function:
function initializeView() {
var deferred = $q.defer();
if(this.momentArray) {
core.listMoments(constants.BEST_MOMENT_PREFIX, '').then(function(moments) {
//Ommitted
deferred.resolve(moments);
}, function(error) {
console.log("ERROR");
deferred.reject(error);
});
}
else {
deferred.resolve();
}
return deferred.promise;
};
The function calls core.listMoments:
function listMoments(prefix, startAfter) {
// var deferred = $q.defer();
var promises = [];
return awsServices.getMoments(prefix, startAfter).then(function(moments) { //Mocked
console.log("getMoments Returned"); //Does not print
for(var i = 0; i < moments.length; i++) {
// moments[i].Key = constants.IMAGE_URL + moments[i].Key;
promises.push(getMomentMetaData(moments[i]));
}
return $q.all(promises);
});
};
Here is my test function:
it('Should correctly initialize the view', function(done) {
spyOn(awsServices, 'getMoments').and.callFake(function() {
console.log("getMoments Has been mocked"); //This prints
return $q.resolve(mock_moment);
});
service.initializeView().then(function() {
done();
})
});
The problem is with the awsServices 'getMoments' mock. The call to awsServices.getMoments is in the listMoments function. I would like to mock out this function but when I do it does not execute the "then" part of the promise.
So based on my console logs it would print the 'getMoments Has been mocked' log but it would not print 'getMoments Returned' log. So the function is mocked but for some reason it is not moving into the then statement and my test just times out.
In order to get the .then() part of a promise to work in such a test, you need to use a $rootScope.$apply(). This is needed whether the promise is in your test code or in a referenced library that is being tested. Think of it like the flush() function for $http or $timeout calls.
The Testing example from the Angular documentation's $q page shows how to use it:
it('should simulate promise', inject(function($q, $rootScope) {
var deferred = $q.defer();
var promise = deferred.promise;
var resolvedValue;
promise.then(function(value) { resolvedValue = value; });
expect(resolvedValue).toBeUndefined();
// Simulate resolving of promise
deferred.resolve(123);
// Note that the 'then' function does not get called synchronously.
// This is because we want the promise API to always be async, whether or not
// it got called synchronously or asynchronously.
expect(resolvedValue).toBeUndefined();
// Propagate promise resolution to 'then' functions using $apply().
$rootScope.$apply();
expect(resolvedValue).toEqual(123);
}));
Note that they inject $rootScope.
$q promises can be synchronous (when they are resolved synchronously) and depend on digest cycles.
There should generally be no asynchronous done callback in Angular tests.
Angular tests are supposed to be synchronous, so are $q promises. In order to achieve that a digest should be triggered manually when an existing promise (the ones that is returned from getMoments and initializeView) is chained with then. If done callback is placed inside then and a digest is not triggered, this will result in spec timeout.
spyOn(awsServices, 'getMoments').and.callFake(function() {
console.log("getMoments Has been mocked"); //This prints
return $q.resolve(mock_moment);
});
service.initializeView();
$rootScope.$digest();
The thing that can be improved here is isolation. There are several units (methods) involved in a single test. This will affect troubleshooting when one of them fails.
Usually unit testing implies that only one unit is tested at time, while the rest are mocked or stubbed. In this case in one test service.listMoments is called and awsServices.getMoments is mocked, and in another test service.initializeView is called and service.listMoments is mocked.
Constants.getContants is a promise which will get all the constants that are used in my application. I would like to save this to a $scope variable so that I can access it anywhere in the controller or application. Now, whenever I need to access it, I need to repeat the call and do the operation there itself.
Even If I try to save it in the $scope it will not be available outside the corresponding handler. How to solve this issue.
Following is the code that I'm using:
Constants.getConstants().then(function (AppContants) {
$scope.responseCount = AppContants.data.serverUrl+AppContants.data.appId
console.log($scope.responseCount);
//$scope.$apply();
});
console.log($scope.responseCount);
Here AJAX call is going out of sync also. I know that actions need to performed inside the handler function so that we can be sure that the intended action is executed only after a successful AJAX call. I need to use these variables outside the function. I tried $scope.$apply() operation as well. It didn't help. Is there a way to solve this? Thanks in advance.
Constants.getConstants().then(function(response)
{
$scope.responseCount = response.data;
}, function(error)
{
console.log(error);
});
And in your service you should have something like
this.getConstants= function($username){
var endpoint = "url";
return $http({
method: 'get',
url: endpoint
});
};
In your case the 2nd Console.Log executes just after placing the AJAX call. It does not wait for the AJAX to respond as it is a asynchronous call.
You can only be able to use '$scope.responseCount' property after the AJAX cal is resolved.
As a workaround you can:
Place this call to fetch constants at the time of application startup and save the constants in some service (shared service).
Do your operation in the 'then' block of this AJAX call.
Here's the thing. When you are Constants.getConstants() it return response as promise. since javascript asynchronous it does not wait until response return. It just keep on executing. That's why the console outside the then function display undefined.
workaround is you can add a function inside promise and put you operations inside that function
Constants.getConstants().then(function(AppContants) {
$scope.responseCount = AppContants.data.serverUrl + AppContants.data.appId
console.log($scope.responseCount);
sampleFunc()
});
function sampleFunc() {
// do your oprations here
console.log($scope.responseCount);
}
You can cache the promise in a service:
app.service("ConstantCache", function(Constants) {
var promiseCache;
this.getPromise = function() {
if ( promiseCache } {
return promiseCache;
} else {
promiseCache = Constants.getConstants();
return promiseCache;
};
};
this.trashCache = function() {
promiseCache = null;
};
});
Then the cached promise can be used in any controller as many times as desired:
ConstantCache.getPromise().then(function(AppContants) {
$scope.responseCount = AppContants.data.serverUrl + AppContants.data.appId
console.log($scope.responseCount);
sampleFunc()
});
-- EDIT --
I encountered a weird thing recently about promises, but I guess it's maybe because it's against the philosophy of promises.
Considering the following code :
// Assuming Auth is just a simple lib doing http requests with promises
Auth.signup()
.then(succCall, errCall)
.then(loginSucc, loginErr)
// My callbacks here
function succCall (){
// OK, send second promise
console.log('succCall');
return Auth.login();
}
function errCall(){
// I do some things here and now
// I want to break out from here
console.log('errCall');
}
function loginSucc(){
// This is the callback of the login method when it went OK
// I want to enter here ONLY if with go through the succCall
console.log('loginSucc');
}
function loginErr(){
// This is the callback of the login method when it went not ok
// I want to enter here ONLY if with go through the succCall
console.log('loginErr');
}
Here if something goes wrong in Auth.signup(), this is what show :
errCall, loginSucc
if i do a $q.reject() in the errCall this is what happens :
errCall, loginErr
and this is what i want :
errCall... finish, stop here
Now, the problem is, it goes in errCall when signup goes wrong, that's good, but then it enters loginSucc...
I want to break out of the then chain when any errorCallback (which is errCall or loginErr here) is encountered.
-- EDIT --
I think i was misunderstood by some mean, i want to totally break the chain without check in any other "then" if something went wrong.
As if i was saying : if first then is wrong stop here, if first then ok continue, if second "then" ok continue, if third "then" wrong, stop
// Just like if i did the following but by chainning "then" methods
// My callbacks here
function succCall (){
// OK, send second promise
return Auth.login().then(loginSucc, loginErr);
}
My point is, i don't want only one error handler if i have many "then" chained
What is effectively happening is this:
try {
try {
var a = succCall();
} catch(e1) {
a = errCall(e1);
}
var b = loginSucc(a);
} catch(e2) {
b = loginErr(e2);
}
You can break out of the chain by calling
return $q.reject('Reason Err was called');
in your errCall() function.
EDIT:
As OP remarked by calling $q.reject the code will enter the loginErr function.
Alternatively you can modify your code like this:
Auth.signup()
.then(function(a) {
succCall()
return loginSucc(a).then(null, loginErr);
}, errCall)
You can read more in these two SO question:
Break promise chain
Break Out of then promises in
Angularjs
This also is a helpful read : Flattening Promise Chains
errCall function needs tor return a promise, and that promise needs to be rejected for loginErr to be fired.
function errCall(){
// i do some things here and now
return $q(function(resolve, reject) {
// auto reject
reject();
});
}
Alternatively try .catch:
Auth.signup()
.then(succCall)
.then(loginSucc)
.catch(function(err){
// caught error, problem is you won't know which function errored out, so you'll need to look at the error response
});
Just don't pass any errCall or loginErr to then and use catch() in the end of the promise chain and it will be interrupted on first error, which will be passed to catch(). If you want explicitly process error of Auth.signup() then your errCall should look like this:
function (err) {
if(isFatal(err)) {
return Promise.reject(new Error('Fatal!')); //`catch()` handler will be called with `new Error('Fatal!')`
} else {
return 'something'; //next `then()` handler will be called with 'something'
}
}
Your best option is to return a promise that is never resolved from errCall():
function errCall() {
console.log('errCall');
return $q.defer().promise;
}
But you are right, what you are trying to do is "against the philosophy of promises".
Why you shouldn't do it
It is of course a problem that your loginSucc function is called when an error occurs during evaluation of the first promise. However, that can be fixed by rejecting once again in errCall as others have already pointed out. But you should not try to avoid the execution of loginErr, otherwise something is conceptually wrong with your code.
The evaluation of the expression
Auth.signup().then(succCall, errCall)
yields another promise. The contract is that a promise has a then() method taking two callbacks as parameters, one for success and one for failure. The contract is also that the success callback is called when the promise is evaluated successfully and that the error/failure callback is called in all other cases. If you intend to never call either of those, don't use a promise!