How to perform same action regardless of promise fulfilment? - javascript

I would like to perform the same action after a promise has either been fulfilled with a success result or failure, ie I want to perform the same action for the success and error handler and then continue to send down the result of the promise to the appropriate erroe/success handlers.
var pleaseWaitPromise = playAudioAsync("please wait");
myLongRunningPromise().then(function tempSuccessHandler(result) {
pleaseWaitPromise.cancel();
return result;
}, function tempErrorHandler(error) {
pleaseWaitPromise.cancel();
return WinJS.Promise.wrapError(error);
}).done(function realSuccessHandler(result) {
console.info(result);
}, function realError(error) {
console.error(error);
});
Is there a more elegant way to stop the pleaseWaitPromise, which could also be a function call instead of a promise (like clearInterval)

jfriend is right you'd typically want finally here - it does exactly what your code above does. Sadly WinJS promises do not feature .finally at the moment so unless you'd like to shim it (patching it on the prototype of WinJS.promise) you're stuck with it.
You can also put it as a function:
function always(prom, fn){
return prom.then(function(v){ fn(v); return v; },
function(e){ fn(e); return WinJS.Promise.wrapError(error); });
}
Which would look like:
always(myLongRunningPromise(),
pleaseWaitPromise.cancel();
})).done(function realSuccessHandler(result) {
console.info(result);
}, function realError(error) {
console.error(error);
});

Sorry, but I don't understand the extra step, wouldn't this just do what you want?
var pleaseWaitPromise = playAudioAsync("please wait");
myLongRunningPromise().then(function tempSuccessHandler(result) {
pleaseWaitPromise.cancel();
console.info(result);
}, function tempErrorHandler(error) {
pleaseWaitPromise.cancel();
console.error(error);
});
edit: second try
I know it is a known anti-pattern, but what if you return a Promise that never fails? Something like:
function neverFails(myLongRunningPromise, pleaseWaitPromise){
return new WinJS.Promise(function (complete){
myLongRunningPromise().then(function () {
pleaseWaitPromise.cancel();
console.info(result);
return complete();
}, function (error) {
pleaseWaitPromise.cancel();
console.error(error);
return complete();
});
});
}
Does that make sense?

Related

Returning Value from a Function that has Chain Ajax Requests in Jquery

I have a chained Ajax requests in my function.
Function A and B are Ajax requests. A will run first and B will run after A returns its data.
My problem is that Function C is the one that executes Function B. When Function C is executed, a button that executes C will be disabled temporarily until A && B is finished.
How do I return a value on function c below?
I need to return true on the function c AFTER function a & b requests completed returning data.
I'm thinking if I return true on the callback function inside done(), fail() or always(), the value will return to the callback function and not to the c().
function a(methodA) {
return $.ajax({
type: "get",
url: "api/a",
cache:false,
data: { method: methodA },
dataType: "json",
});
}
function b(methodB) {
return $.ajax({
type: "get",
url: "api/b",
cache:false,
data: { method: methodB },
dataType: "json",
});
}
function c(data) {
a(data)
.done(function(data2) {
b(data2)
.done(function(data3) {
})
.fail(function(data3) {
})
.always(function(data3) {
})
})
.fail(function(data3) {
})
}
$(document).ready(function() {
$('#btnC').click(function(event) {
$('#btnC').prop('disabled', true);
var c = c(getData);
if(c == true) {
$('#btnC').prop('disabled', false);
}
});
});
jQuery Ajax method returns a promise object. (To be exact, jqXHR)
Usually, it uses like this.
var result =
$.post("url").done(function() {
// after ajax.
});
// called when ajax process done finished.
result.promise().done(function() {
// blah blah blah
});
However, when it comes to a nested ajax, you need to more work than usual.
Use then() and return inner ajax.
function callAjaxs() {
return $.get("ajaxA").then(function(res) {
return $.get("ajaxB", res).done(function() {
// blah blah blah
});
});
}
// call function and use promise. (of course, you can also use fail, always)
callAjaxs().promise().done(function() {
// $('#btnC').prop('disabled', false);
});
If you don't care about details, I think this way is a good option.
If you want the return value of c to be dependent on asynchronous callbacks you can't just return a value. You have to return a promise because the value you want to return is timed based on the asynchronous requests made by the 2 ajax calls (not available when return is called). Therefor you can promise the return of a value but cannot give the value right away.
function a() {
// mock request since content doesn't matter
return $.get('https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js');
}
function b(fail) {
// google does not allow cross origin requests
if (fail) return $.get('https://google.com');
return a();
}
function c(failB) {
let requests = [
Promise.resolve( a() ),
Promise.resolve( b(failB) ),
];
return Promise
.all(requests)
.then(results => Promise.resolve(true))
.catch(error => Promise.reject('one or multiple requests failed'));
}
c().then(result => {
console.log(result);
}).catch(error => {
console.log(error);
});
c(true).then(result => {
console.log(result);
}).catch(error => {
console.log(error);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Returning just the value true might be a bit useless, since if the promise is resolved you know everything succeeded. You could just simply leave out that part and the results will be passed on as result of the c promise. The same goes for the error message. In the above code I don't use the error itself but instead generate my own string. If you are more interested in the failing object you could leave out this catch in c. See the example below.
function a() {
// mock request since content doesn't matter
return $.get('https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js');
}
function b(fail) {
// google does not allow cross origin requests
if (fail) return $.get('https://google.com');
return a();
}
function c(failB) {
let requests = [
Promise.resolve( a() ),
Promise.resolve( b(failB) ),
];
return Promise.all(requests);
}
c().then(results => {
// in this case has 2 the same values since the same url is requested
results.forEach(resp => console.log(resp.substring(0, 15)));
}).catch(jqXhr => {
console.log(jqXhr.statusText);
});
c(true).then(results => {
results.forEach(resp => console.log(resp.substring(0, 15)));
}).catch(jqXhr => {
console.log(jqXhr.statusText);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Jasmine - How to chain a `.then` and a `.finally` in a callFake spy?

I have the following function, that does a service call with a promise and a .finally:
myService.getStuff().then(function() {
this.doStuffWhenServiceOK();
}, function () {
this.doStuffWhenServiceFails();
}).finally(function() {
this.doFinally();
});
I am spying on this service with the following spy:
spyOn(myService, 'getStuff').and.callFake(function() {
return {
then: function (succesFn, errorFn) {
return succesFn();
}
};
});
The problem is that the test complains that the .finally is not known. Just adding it after .then does not seem to be a solution...
return {
then: function(successFn) {
return successFn();
},
finally: function(successFn) {
return successFn();
}
}
Who knows how to chain .then and .finally in the callFake spy?
I work with Angular 1.
Return a finally function.
function then(succesFn, errorFn) {
succesFn();
return {finally:function() {}};
}

Promise failing in an array of true/false values

I have an array of validation checks and one happens to be using ajax to check for an address. I needed the entire array to return true and fire sequentially before submitting a form. I tried using promises but to no avail.
Here's the issue. If I either enter an incorrect address or leave the input(s) blank then it doesn't submit the form (good). But when I actually enter a valid address the form submits despite the fact that my other validations in the validations array have false values. What am I doing wrong?
var validations = [validateInputPresence, validatePassword, validateAddress];
$continueButton.on('click', function() {
toggleSpinner();
subscribe().then(function() {
submitForm(); // This is firing even when some values are false in the array
}, function() {
toggleSpinner();
});
});
function subscribe() {
var promises = validations.map(function(validation) {
return validation();
});
return Promise.all(promises);
}
function validatePassword() {
var password = $password.val();
var format = /^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])[^+&\n]{8,}$/;
return validateInput(format.test(password));
}
function validateAddress() {
return new Promise(function (resolve, reject) {
$.ajax({
type: 'POST',
url: '/address/validate',
data: $form.serialize(),
dataType: 'json',
success: function(response) {
var hasValidAddres = response.Data === 200;
validateInput(hasValidAddres);
hasValidAddres ? resolve() : reject();
},
error: function() {
toggleSpinner();
}
});
});
}
function validateInput(validation) {
if (validation) {
return true;
} else {
return false;
}
}
The main problem is that you don't return the Promise in the validateInput() function. You can't reject a promise returned by Promise.all via returning a false in one of its function.
Read more about Promise.all, the quote below is taken from MDN
If something passed in the iterable array is not a promise, it's converted to one by Promise.resolve.
So actually anything, but Promise will be treated as being resolved.
What you should do is write the validateInput function to return a promise.
function validateInput(validation) {
return new Promise(function(resolve, reject) {
if (validation) {
resolve();
} else {
reject();
}
});
}
What am I doing wrong?
You've never tested whether the values in the array were true or false. You did most of the promise stuff correctly, and subscribe() returns a promise for an array of boolean values. If you want to test whether all of them are true, you will need to do that explicitly:
subscribe.then(function(results) {
if (results.every(Boolean)) // all are true
submitForm();
else // some are false
…;
}, function(err) {
// some validations threw an exception
});

Rewriting this frankenstein promise chain

So i brought to life this abomination and i couldnt for the life of me get my head around how to optimise it in such a way i can run this chain properly using Promise.all / Promise.join.
Anyone able to point me in the right direction? Should probably separate the methods first.
Any insight is appreciated.
getOpenIDConf: function() {
return client
.getAsync('openId')
.then(
function(result) {
if (!result) {
return request
.getAsync({
url: 'https://accounts.google.com/.well-known/openid-configuration',
json: true
}).spread(
function(response, body) {
var result = JSON
.stringify(body);
client.setAsync('openId',
result).then(
function() {
return result;
});
});
} else {
return result;
}
});
},
[EDIT] To clarify, i'm using bluebird
Refactoring a bit and changing the code style gives this.
getOpenIDConf: () => client.getAsync('openId').then(result =>
result || request.getAsync({
url: 'https://accounts.google.com/.well-known/openid-configuration',
json: true
}).get(1).then(JSON.stringify).then(result =>
client.setAsync('openId', result).return(result);
)
)
},
A few features of a good promise library (not sure which one you are using) is that you can chain the promises like so:
doSomething(function(result) {
return doSomethingElse();
}).then(function(result2) {
return doSomethingElseAgain();
}).then(function(result3) {
// It all worked!
}).catch(function() {
// Something went wrong
});
Or you can wait for a set of them to complete:
var promiseArray = [];
promiseArray.push(doSomething());
promiseArray.push(doSomethingElse());
promiseArray.push(doSomethingElseAgain());
Promise.all(promiseArray).then(function() {
// It all worked!
}).catch(function() {
// Something went wrong
});
Hope this is informative.

Order function call

I need to make submitAdapterAuthentication() function to work the first getUserRoles() function, but with the current implementation of the getUserRoles() function is being executed first that submitAdapterAuthentication(). How can I fix this?
checkOnline().then(function(onl) {
userObj.isLoginOnline = onl;
}).then(function() {
submitAdapterAuthentication(user, pass);
}).then(function() {
getUserRoles();
});
function submitAdapterAuthentication(user, pass) {
var invocationData = {
parameters : [ user, pass ],
adapter : "adapterAuth",
procedure : "submitLogin"
};
ch.submitAdapterAuthentication(invocationData, {
onFailure : function(error) {
WL.Logger.log("ERROR ON FAIL: ", error);
},
onSuccess : function() {
WL.Client.updateUserInfo({
onSuccess : function() {
//return promise
WL.Client.updateUserInfo({
onSuccess : function() {
}
});
}
});
}
});
}
// my function to obtain roles
// It should be performed after submitAdapterAuthentication
function getUserRoles(){
var arrayRoles = [];
var attributes = WL.Client.getUserInfo(realm, "attributes");
if(attributes){
if(attributes.roles){
arrayRoles.push(attributes.roles);
}
}
}
When chaining promises, if you return anything but another promise from a then() callback, the resulting promise will be resolved immediately with the value undefined.
In order to make sure your callbacks are executed in the order you specified, just make sure each callback is returning a promise at the end. If you want to return some value from a callback, wrap it in a $q.when(). In this case it looks like you are not using any intermediary return values, so you can just wrap any arbitrary value in a $q.when() to make sure a promise is returned:
checkOnline().then(function(onl) {
userObj.isLoginOnline = onl;
return $q.when(true);
}).then(function() {
submitAdapterAuthentication(user, pass);
return $q.when(true);
}).then(function() {getUserRoles();});
Based on your latest edit, it looks like ch.submitAdapterAuthentication() is likely returning a promise. If this is the case, you should return this promise from the function:
return ch.submitAdapterAuthentication(invocationData, {...
And then return this promise in the then callback:
then(function() {return submitAdapterAuthentication(user, pass);})
If ch.submitAdapterAuthentication() does not return a $q promise, you will have to wrap it yourself:
var deferred = $q.defer();
ch.submitAdapterAuthentication(invocationData, {
onFailure : function(error) {
WL.Logger.log("ERROR ON FAIL: ", error);
deferred.reject(error);
},
onSuccess : function() {
WL.Client.updateUserInfo({
onSuccess : function() {
deferred.resolve();
}
});
}
});
return deferred.promise;

Categories