Javascript Promises recall until acceptable return - javascript

ES6
I want to keep calling this method until it returns 0 as the result. Sometimes it takes 5 seconds to return a value, sometimes it takes 10 seconds. Sometimes it doesnt return the value but i want to keep going until it returns 0. It should wait until the answer has come back or timeout in 10 seconds to recall it.
This is how i tried this, but it is not working for some reason.
function method() {
doSomething(from_api).then(function (status) {
if(status != "0") {
method();
} else {
console.log("success");
}
}), function (error) {
method();
});
}
What is the right way to do this?

I'm guessing you want something like an asynchronous-while-loop.
function asynchronous_while(promise_provider, condition) {
return promise_provider().then(function (result) {
if (condition(result) === true) {
return asynchronous_while(promise_provider, condition);
} else {
return result;
}
});
}
asynchronous_while(function () { // promise provider
return getStatus(from_api);
}, function (result) { // condition
return result != "0";
}).then(function (result) {
console.log("success", result);
// result should now be "0";
});
How long it takes to complete depends entirely on when getStatus(from_api) returns 0. That might be never. The same pitfalls of a traditional while apply.

Your code works. You just had an extra closing parenthesis.
Change:
}), function (error) {
to:
}, function (error) {

Related

Condition check for multiple Async functions

I am using Ionic 3 with Angular.
I have multiple asyn functions:
async buildNewsItemsViaHttp(){
let result = await this.http.get()....
}
async buildNewsItemsViaLocalJSON(){
return await this.http.get()....
}
async getNewsItems(){
return await this.storage.get()...
}
I would like to run the async functions conditionally based on if they return a value or not. This means:
if getNewsItems() would return data, return it
if not, run buildNewsItemsViaHttp and see if it returns data
if not, run buildNewsItemsViaLocalJSON
Suggestion:
async getItems(){
let result = await.this.getNewsItems();
if (result) {
return result;
} else {
result = await.this.buildNewsItemsViaHttp();
if (result) {
return result;
} else {
result = await.this.buildNewsItemsViaLocalJSON();
return result;
}
}
}
Is this solution correct?
Thanks in advance
You should use sort of chaining to achieve what you want, this below is a bit naive but should work:
getNewsItems().then((result)=>{
if (result === "what you need") {
// do here what you want with the result data
} else {
buildNewsItemsViaHttp().then((result)=>{
if (result === "what you need") {
// do here what you want with the result data
} else {
buildNewsItemsViaLocalJSON.then((result)=>{
// do here what you need with result
})
}
})
}})
And of course you should also have logic to catch errors (.catch(err)=>{})

How to break out of the promise chain in the middle

Here is my sample code.
Orchestrator calls the worker with couple of inputs first, upon getting the response, it has to validate whether the response was satisfactory or not.
If satisfactory, just return to the caller.
If not, again, call the same worker or may be different worker with slightly different input and follow the flow.
Here, although, my code calls cb() after the first worker call, it's also going to the second then and errors out "response" is undefined etc..
I could add an extra condition to check whether the 1st response was satisfactory, like need2ndworkercall && validate(response) in 2nd then and get away with it. But wondering what is the right way of dealing with this problem. Appreciate any feedback.
function orchestrateSomething(input, cb){
doSomething(input.a, input.b)
.then(response=>{
if(validate(response)){
cb(buildResultObj(response));
}
else{
return doSomething(input.a)
}
})
.then(response=>{
if(validate(response)){
cb(buildResultObj(response));
}
else{
cb(null,{});
}
})
.catch(error=>cb(error));
}
return value from function and .then(). Also cb function should call passed function which returns value or evaluate parameters and return value passed
function orchestrateSomething(input, cb){
return doSomething(input.a, input.b)
.then(response=>{
if(validate(response)){
return cb(buildResultObj(response));
}
else{
return doSomething(input.a)
}
})
.then(response=>{
if(validate(response)){
return cb(buildResultObj(response));
}
else{
return cb(null,{});
}
})
.catch(error=>cb(error));
}
orchestrateSomething(input, cb) // where `cb` calls function or values passed
.then(function(results) {
console.log(results)
})
.catch(function(err) {
console.log(err)
});
It is possible break the promise chain via simple throw. The trick is to handle it properly on the catch call:
doPromise(...)
.then(...)
.then(result => {
if(condition) {
throw result
}
else {
return doPromise()
}
})
.then(...)
.catch(result => {
if(result instanceof Error) {
// handle error result
}
else {
// handle desired result
}
})
Here's the simpliest demo of such approach: http://plnkr.co/edit/H7K5UsZIueUY5LdTZH2S?p=preview
By the way, if you can generalize then processing function, it becomes possible to make a recursive call:
processCB = (result) => {
if(condition) {
throw result
}
else {
return doPromise()
}
}
catchCB = (result) => {
if(result instanceof Error) {
// handle error result
}
else {
// handle desired result
}
}
doProcess = () => doPromise()
.then(processCB)
.catch(catchCB)
And here's the demo for the second piece: http://plnkr.co/edit/DF28KgBOHnjopPaQtjPl?p=preview

How do you repeat an ajax call and be able to cancel it if needed in angular?

I have 3 ajax calls that need to be run serially. My issue is how do I do this with $interval. Is there another way to accomplish this?
I have these calls.
var server1 = getDataFromServer1()
var server2 = getDataFromServer2UsingServer1Data(server1);
var server3 = getDataFromServer3UsingServer2Data(server2);
However I want to repeat them but be able to cancel them if I get a certain value. Won't $interval run getDataFromServer1() before I get data from getDataFromServer3UsingServer2Data since it won't wait. Whats the best way to accomplish this? Thanks.
I am not sure whether this is correct approach or not but you can try something like this:
function getDataFromServer() {
getDataFromServer1().then(function(res1) {
if (value === YOU_CERTAIN_VALUE) {
//do what you want
return
}
getDataFromServer2UsingServer1Data(server1).then(function(res2) {
if (value === YOU_CERTAIN_VALUE) {
//do what you want
return
}
getDataFromServer3UsingServer2Data(server2).then(function(res3) {
if (value === YOU_CERTAIN_VALUE) {
//do what you want
return;
}
//calling the function again.
getDataFromServer();
});
});
});
}
This can be done using promise.
You should return a promise from each of your server call like below,
function getDataFromServer1(){
return $http.get('your_url', {options});
}
Here you are return the result from the $http. $http will return a promise so that you can handle this promise while calling this function.
Now you can achieve your requirement like this,
getDataFromServer1().then(function(response){
if(response.data == someValue){
// if you condition satisfied then call next server call
getDataFromServer2UsingServer1Data().then(function(response){
if(some_condition){
getDataFromServer3UsingServer2Data().then(function(response) {
});
}
});
}
});
Chaining promises is the better way to handle errors and elegant when there is such dependencies.
function getDataFromServer1() {
return $http
.get('/some/url/in/server1')
.then(function(response) {
$scope.server1Data = response.data; // If this data is needed in scope
return response.data;
});
}
function getDataFromServer2WithServer1Data(server1Data) {
if(server1Data.condition === 'not met') {
return $q.reject('server1 condition not met');
}
return $http
.get('/some/url/in/server2')
.then(function(response) {
$scope.server2Data = response.data; // If this data is needed in scope
if(server2Data.condition === 'not met') {
throw 'server2 condition not met'; // other way to abort the chain, it reaches the final catch block
}
return response.data;
});
}
function getDataFromServer3WithServer2Data(server2Data) {
return $http
.get('/some/url/in/server3')
.then(function(response) {
$scope.server3Data = response.data; // If this data is needed in scope
return response.data;
});
}
getDataFromServer1()
.then(getDataFromServer2WithServer1Data)
.then(getDataFromServer3WithServer2Data)
.catch(function(rejectReason){
console.log('error occured', rejectReason);
});
This blog explains more about this scenario, would recommend to go through it.

Return not firing

In an $.ajax callback, I want to do something depending on what I receive from the server, which is sending true or false.
The problem is, somewhere in the code, I want a return to fire and it doesn't :
function check()
{
// $.ajax callback(result)
{
console.log(result); //in this case I get 'true' (boolean)
if(!result)
{
// I checked I don't end up here
}
else
{
console.log('well done'); // shows up in the console
return "done";
}
}
return "oops";
}
// later in the code
console.log(check()); // displays "oops" whereas the console.log('well done') has showned up in the console
Parts of the function I didn't give you are mostly CSS effects.
Do you have any idea why a return couldn't fire, or what did I miss ? Thanks in advance !
You are returning a value in a callback, this is why you don't get this value, your code is equivalent to:
function callback (result) {
if (!result) { }
else {
console.log('well done') ;
return "done";
}
}
function _ajax (cb) {
cb (true) ; // Call cb but does not care about the result
return true ;
}
function check() {
ajax (callback) ; // Call ajax but does not care about its result
return "oops";
}
check () ;
If you were doing an asynchronous request, the behaviour would be a bit different, but the idea would remain the same.
You should never do synchronous (a)jax calls, but if you did not care, you could do the following:
function check () {
var res ;
$.ajax ({
success: function (result) {
if (!result) { res = "fail" ; }
else {
console.log('well done') ;
res = "done" ;
}
}
}) ;
return (typeof res === 'undefined') ? "oops" : res ;
}

how to break promise chain

I a promise in such fashion,
function getMode(){
var deferred = Promise.defer();
checkIf('A')
.then(function(bool){
if(bool){
deferred.resolve('A');
}else{
return checkIf('B');
}
}).then(function(bool){
if(bool){
deferred.resolve('B');
}else{
return checkIf('C');
}
}).then(function(bool){
if(bool){
deferred.resolve('C');
}else{
deferred.reject();
}
});
return deferred.promise;
}
checkIf returns a promise, and yes checkIf cannot be modified.
How do I break out of the chain at the first match? (any way other than explicitly throwing error?)
Any way other than explicitly throwing error?
You may need to throw something, but it does not have to be an error.
Most promise implementations have method catch accepting the first argument as error type (but not all, and not ES6 promise), it would be helpful under this situation:
function BreakSignal() { }
getPromise()
.then(function () {
throw new BreakSignal();
})
.then(function () {
// Something to skip.
})
.catch(BreakSignal, function () { })
.then(function () {
// Continue with other works.
});
I add the ability to break in the recent implementation of my own promise library. And if you were using ThenFail (as you would probably not), you can write something like this:
getPromise()
.then(function () {
Promise.break;
})
.then(function () {
// Something to skip.
})
.enclose()
.then(function () {
// Continue with other works.
});
You can use
return { then: function() {} };
.then(function(bool){
if(bool){
deferred.resolve('A');
return { then: function() {} }; // end/break the chain
}else{
return checkIf('B');
}
})
The return statement returns a "then-able", only that the then method does nothing.
When returned from a function in then(), the then() will try to get the result from the thenable.
The then-able's "then" takes a callback but that will never be called in this case. So the "then()" returns, and the callback for the rest of the chain does not happen.
I think you don't want a chain here. In a synchronous fashion, you'd have written
function getMode(){
if (checkIf('A')) {
return 'A';
} else {
if (checkIf('B')) {
return 'B';
} else {
if (checkIf('C')) {
return 'C';
} else {
throw new Error();
}
}
}
}
and this is how it should be translated to promises:
function getMode(){
checkIf('A').then(function(bool) {
if (bool)
return 'A';
return checkIf('B').then(function(bool) {
if (bool)
return 'B';
return checkIf('C').then(function(bool) {
if (bool)
return 'C';
throw new Error();
});
});
});
}
There is no if else-flattening in promises.
I would just use coroutines/spawns, this leads to much simpler code:
function* getMode(){
if(yield checkIf('A'))
return 'A';
if(yield checkIf('B'))
return 'B';
if(yield checkIf('C'))
return 'C';
throw undefined; // don't actually throw or reject with non `Error`s in production
}
If you don't have generators then there's always traceur or 6to5.
You could create a firstSucceeding function that would either return the value of the first succeeded operation or throw a NonSucceedingError.
I've used ES6 promises, but you can adapt the algorithm to support the promise interface of your choice.
function checkIf(val) {
console.log('checkIf called with', val);
return new Promise(function (resolve, reject) {
setTimeout(resolve.bind(null, [val, val === 'B']), 0);
});
}
var firstSucceeding = (function () {
return function (alternatives, succeeded) {
var failedPromise = Promise.reject(NoneSucceededError());
return (alternatives || []).reduce(function (promise, alternative) {
return promise.then(function (result) {
if (succeeded(result)) return result;
else return alternative();
}, alternative);
}, failedPromise).then(function (result) {
if (!succeeded(result)) throw NoneSucceededError();
return result;
});
}
function NoneSucceededError() {
var error = new Error('None succeeded');
error.name = 'NoneSucceededError';
return error;
}
})();
function getMode() {
return firstSucceeding([
checkIf.bind(null, 'A'),
checkIf.bind(null, 'B'),
checkIf.bind(null, 'C')
], function (result) {
return result[1] === true;
});
}
getMode().then(function (result) {
console.log('res', result);
}, function (err) { console.log('err', err); });
i like a lot of the answers posted so far that mitigate what the q readme calls the "pyramid of doom". for the sake of discussion, i'll add the pattern that i plunked out before searching around to see what other people are doing. i wrote a function like
var null_wrap = function (fn) {
return function () {
var i;
for (i = 0; i < arguments.length; i += 1) {
if (arguments[i] === null) {
return null;
}
}
return fn.apply(null, arguments);
};
};
and i did something totally analogous to #vilicvane's answer, except rather than throw new BreakSignal(), i'd written return null, and wrapped all subsequent .then callbacks in null_wrap like
then(null_wrap(function (res) { /* do things */ }))
i think this is a good answer b/c it avoids lots of indentation and b/c the OP specifically asked for a solution that doesn't throw. that said, i may go back and use something more like what #vilicvane did b/c some library's promises might return null to indicate something other than "break the chain", and that could be confusing.
this is more a call for more comments/answers than a "this is definitely the way to do it" answer.
Probably coming late the party here, but I recently posted an answer using generators and the co library that would answer this question (see solution 2):
https://stackoverflow.com/a/43166487/1337392
The code would be something like:
const requestHandler = function*() {
const survey = yield Survey.findOne({
_id: "bananasId"
});
if (survey !== null) {
console.log("use HTTP PUT instead!");
return;
}
try {
//saving empty object for demonstration purposes
yield(new Survey({}).save());
console.log("Saved Successfully !");
return;
}
catch (error) {
console.log(`Failed to save with error: ${error}`);
return;
}
};
co(requestHandler)
.then(() => {
console.log("finished!");
})
.catch(console.log);
You would pretty much write synchronous code that would be in reality asynchronous !
Hope it helps!
Try to use libs like thisone:
https://www.npmjs.com/package/promise-chain-break
db.getData()
.then(pb((data) => {
if (!data.someCheck()) {
tellSomeone();
// All other '.then' calls will be skiped
return pb.BREAK;
}
}))
.then(pb(() => {
}))
.then(pb(() => {
}))
.catch((error) => {
console.error(error);
});

Categories