RxJS Approach leads to Callback Hell - javascript

I would like to use ReactiveJS Observable method for the below use case.
IF MAIN_CACHE EXIST
RETURN OUTPUT
ELSE IF DB CONNECTION EXIST
CACHE MAIN_CACHE (1 Hour)
CACHE FALLBACK_CACHE (3 Days)
RETURN OUTPUT
ELSE IF FALLBACK_CACHE EXIST
RETURN OUTPUT
I got the expected output but i feel this leads to Callback Hell and I think, still its not a good approach and i'm missing something in ReactiveJS Observable key benefits.
Below is my code and the whole code is in JS Bin Link
mainCache.subscribe(function (response) {
console.log(response);
}, function (error) {
dbData.subscribe(function (response) {
console.log(response);
}, function (error) {
console.log('DB CAL Log info', error);
fallbackCache.subscribe(function (response) {
console.log('FALLBACK CACHE SERVED');
console.log(response);
}, function (error) {
console.log('DB CAL Log error', error);
});
});
});
Any lights. much appreciated with working example.

Your implementation indeed is callback hell. You can avoid it by composing your observables with the available rxjs operators, and subscribe at the end when you want to receive the data and have no more transformation. You can have a look at How to do the chain sequence in rxjs and Why we need to use flatMap?, to see how to chain operators, and use only one subscribe at the end of the chaining.
Here your chaining comes from catching errors, so like promises, the best practice is to catch early the errors, as you do, and for that you can use the catch operator`.
mainCache
.do(console.log.bind(console))
.catch(err => dbData.catch(err => {
console.log('DB CAL Log info', error);
return fallbackCache.do(response => {
console.log('FALLBACK CACHE SERVED');
console.log(response);
}).catch(console.log.bind(console, `DB CAL Log error`))
}))
This is only tangential to your use case, but for information, I include also this link, which deals with retrial in case of miss (for example from dbData): retryWhen with backoff

Related

Retrying a failed async/promise function?

I have this async block:
test().then(function(result){
// Success: Do something.
doSomething();
}).catch(function(error){
// Error: Handle the error, retry!
// How to re-run this whole block?
});
I can keep track of the success and failed outcomes. However, is it possible to retry the whole test().then().catch() chain if we fail? And keep retrying until the condition resolves?
If you can switch to async/await syntax, you can use a while loop:
let keepTrying;
do {
try {
await test();
keepTrying = false;
} catch {
keepTrying = true;
}
} while (keepTrying)
doSomething();
You could then abstract the retrying logic into its own function to be reused.
Assuming it's all about resending request to some buggy/bloat-up 3rd party API
If it's production question rather educational one I'd suggest search for 3rd party lib that implementing this on your own.
Say for axios there is nice axios-retry.
Why? Assume you may think there is just one case when API say returns 502. But actually there are much more cases it'd be better to keep in mind:
differing particular error causes, say once there is Network or DNS Lookup Error there may be no need to repeat request
retry count limitation
increasing delay
something else
Writing such a logic on your own would be real overkill. And trying to use simplest solution may hit you when you don't expect it.
PS also as a bonus you would be able to configure all requests to some specific API with single snippet like it goes for axios' custom instances(and I believe there should other plugins for alternative libraries)
You could put the whole thing into a function that recursively calls itself if the catch block is entered:
function tryTest() {
return test().then(function(result) {
// Success: Do something.
doSomething();
}).catch(function(error) {
// error handling
// make sure to return here,
// so that the initial call of tryTest can know when the whole operation was successful
return tryTest();
});
}
tryTest()
.then(() => {
console.log('Finished successfully');
});
If your doSomething can take the result argument, and if tryTest doesn't take any arguments, you can simplify the above to:
function tryTest() {
return test()
.then(doSomething)
.catch(tryTest);
}
tryTest()
.then(() => {
console.log('Finished successfully');
});
You can put it in a function.
function dbug() {
test().then(function(result){
// Success: Do something.
doSomething();
}).catch(function(error){
// Error: Handle the error, retry!
dbug()
});
}

Using Angular 2 to call jQuery based Javascript ajax library

I have a feeling that I'm just trying to fit a square peg into a round hole, but I'm trying to apply some things with Angular2 and Typescript, and I'm banging my head against a wall.
I've written a Javascript module that acts as an API client library to an API I'm consuming. It just packages some convenience things like setting up the correct API keys, switching keys based on certain desired data, etc. It's basically just a convenience library.
Most of the methods follow a pattern where you provide a query term and then execute a callback.
So for example:
API.searchAutocomplete("angular", function(err, data) {
// handle the data/error
});
Inside that method:
searchAutocomplete: function(query, callback) {
// set up request with data payload, url, headers, etc
$.ajax(settings)
.done(function(response) {
// callback with success
})
.fail(function () {
// callback with error
});
}
I'm struggling with trying to understand how to run this function in Typescript in an Angular service with a Promise (square peg round hole). Or should I just pass a callback within the service and treat it like it's Javascript?
My attempt:
public getAutocomplete(query:string): Promise < any > {
return new Promise((resolve, reject) => {
API.searchAutocomplete(query, function (err, result) {
if (err) {
reject(err);
return;
}
resolve(result);
});
});
}
Second, I've been able to load the library into my Angular app but I can't seem to actually make any of the requests. Even if I break in the console and access the library object it doesn't seem to actually make any network requests. Which I really don't understand.
Edit: I've sorted this part out.
When I made my service call return a promise, I had to subscribe to the promise otherwise I wouldn't execute it correctly. I think I still need to understand how to write my service call to return an observable and map the callback response.
As expected, I was trying to do more work than I should have.
This is pretty simple, just return an observable that calls the external library.
public autoCompleteResults(query: string): Observable<string[]> {
return new Observable<string[]>(observer => {
API.searchAutocomplete(query, function (err, result) {
if (err) {
console.log(err);
observer.next([]);
// OR
observer.error(err);
return;
}
observer.next(result);
});
});
}

expressJS promise and error handling

I have a route that first need to query the database, then with the results, query another web service, then with that result render the page.
I have that flow worked out and am trying to figure out the error handling. Given that i talk to multiple service, I'm trying to massage the error before returning them to express.
Here is the structure of the code for the route:
Models.Episode.findById(request.params.episodeID)
.catch(function (error) {
throw (throwjs.notFound());
})
.then(function (episode) {
if (episode.getUser().id !== request.user.href) {
return next(throwjs.unauthorized("You do not have access to this podcast"));
}
return doSomeOtherAsyncStuff();
})
.then(function (queryResponse) {
renderPage();
})
.catch(function (error) {
next(error);
});
My problem is with the first catch. My goal in this catch is to repackage the error and stop the execution and send the error to express middleware.
With the way it is written above, the execution stops, but my express error handler are not called.
I tried rewriting the first catch as
.catch(function(error){
return next(error);
})
But that does not solve the issue. The only solution i found is to move the catch to the end. But then i lose context of the failure location.
Any clue as to what i'm doing wrong?
Thanks, olivier
I'd recommend taking a different approach so you don't have to rely on long running promise chains. With the following approach, you've decoupled your authorization and and validation to separate middleware, since they're not necessarily a concern of the actual episode handler itself. Plus this approach is more idiomatic to express.
An added bonus is that you're free to pass errors down to an error handler so you further decouple your errors from your route handlers.
function validateEpisode(req, res, next) {
Models.Episode
.findById(req.params.episodeID)
.then(function(episode) {
req.yourApp.episode = episode;
next() // everything's good
})
.catch(function(error) {
// would be better to pass error in next
// so you can have a general error handler
// do something with the actual error
next(throwjs.notFound());
});
}
function authUserByEpisode(req, res, next) {
if (req.yourApp.episode.getUser().id !== req.user.href) {
next(throwjs.unauthorized("You do not have access to this podcast"));
}
next(); // authorized
}
function episodeController(req, res) {
// do something with req.yourApp.episode
}
app.get('/episode/:id', validateEpisode, authUserByEpisode, episodeController)
Well after all, this is related to the throwjs framework I'm using and the fact that I'm using incorrectly
throw (throwjs.notFound());
should be
throw (new throwjs.notFound());
...

Utilize console logging in native javascript promise chain

I have a native javascript promise chain that looks a little like this:
function chain() {
promiseFunction1().then(function(data) {
console.log("some message");
return promiseFunction2();
}).then(function(data) {
console.log("some message");
return promiseFunction3();
}).then(function(data) {
console.log("some message");
return promiseFunction4();
}).catch(function(error) {
console.error(error.stack);
});
}
the promise functions would look a little like this:
function promiseFunction() {
return new Promise(function(resolve,reject) {
someCallbackfunction(arg, function(err, data){
if (err) {
return reject(err);
}
console.log("some message");
return resolve(data);
});
});
}
My code seems to resolve fine from what I can tell (no errors and I can tell from terminal feedback that the operations I needed started running) but no matter what I seem to try I cannot for the life of me seem to get any form of console logging.
1) Why are these statements not printing like I expect them to?
2) How can I go about getting back my verbose output?
It turned out that in the end the problem was external.
Somewhere in my promise chain I called an external library with a callback function but the library never responded causing my chain to wait forever.
If you experience something similar I encourage you to double check all of the functions in the chain for a similar occurrence.
A quick way to debug this could be to place a timeout in each promise function in the chain that resolves it after x amount of time, that way you can at least get your logging results without having to stumble in the dark forever.

Parse .then method and chaining | Syntax eludes me

Dear stackoverflow community,
I am using application craft as a JS cloud IDE and integrating Parse's cloud database for storage.
I'm writing a subscription service and want to check to see if a user is already subscribed prior to either authenticating their use or prompting them to sign up. Being an asynchronous call I'm trying to utilise Parse's/Promise's .then method to wait for the server's response before proceeding.
I've read the examples and samples on the Parse site (linked before) yet cannot wrap my head around it.
This code functions, and returns the email address if a match is found:
... *Parse declaration etc*
query.find({
success: function(results){
if(results.length>0){
app.setValue("lblOutput", results[0].attributes.email);
}
else{
app.setValue("lblOutput", "No match.");
}
},
error: function(object, error){
console.log(error.message);
console.log(object);
}
});
My attempt at chaining, most recently this:
query.find().then(function(results) {
if(results){
console.log("Within the Then!");
console.log(results);
}
else{
console.log("Could be an error");
}
});
States that method or property 'success' is invalid for undefined. I have attempted to combine the syntax of the first function (success: ... // error: ...) in the chaining attempt unsuccessfully.
Any advice as to how I could
Check to see if an email exists in the Parse DB, then
Wait until the result comes back for further manipulation with another function
would be greatly appreciated.
Once I have .then() figured out there will be further layers of async waiting.
Cheers,
James
Your syntax for handling then is incorrect, it should be:
query.find().then(
function(results) {
console.log("Within the Then!");
console.log(results);
},
function(error){
console.log("Could be an error"+error);
}
);
the then() function takes one or two functions.
the first is a success handler, and the second is an error handler.
query.find().then(success,error)
This snippet is untested but should be pretty close.
var query = new Parse.Query(Parse.User);
query.equalTo(email, "me#me.com");
query.count().then(
function(resultsCount) {
//success handler
if(resultsCount > 0){
doSomeOtherFunction();
}
},
function(error){
//error handler
console.log("Error:"+error);
}
);
If you have more async work to do, your method should look similar to this, remember that when chaining promises the last then() should contain your error handling.
query.find().then(function(result){
doSomethingAsync(); //must return a promise for chaining to work!
}).then(function(result1){
doSomethingAsync2(); //must return a promise for chaining to work!
}).then(function(result2){
doSomethingAsync3(); //must return a promise for chaining to work!
}).then(null,function(error){
// an alternative way to handle errors
//handles errors for all chained promises.
})

Categories