RXJS error handling and wrapping multiple calls - javascript

Is there a better way to orchestrate the below code in Rxjs ?
and from Javascript perspective Promise/Asyc/Await which will be better considering some hard error and soft error handlings.
Couple of calls are hard dependency which are marked as hard true in the error and on subsequent error handler checks and re throw it. and soft errors are added to final data.error for any processing based on the soft errors.
How this can be better done ?
const { Observable } = require('rxjs');
const { map, filter, flatMap, concatMap } = require('rxjs/operators');
function testcallback(fail, callback) {
if (fail == true) {
setTimeout(function(){ callback(new Error({ message: "Operation failed" })); }, 100);
}
else {
setTimeout(function(){ callback(null, 10); }, 100);
}
}
function createData(param) {
return Observable.bindNodeCallback(testcallback)(param);
}
function associateData(param) {
return Observable.bindNodeCallback(testcallback)(param);
}
function removeData(param) {
return Observable.bindNodeCallback(testcallback)(param);
}
function markAssociateSuccess(param) {
return Observable.bindNodeCallback(testcallback)(param);
}
function removeOldData(param) {
return Observable.bindNodeCallback(testcallback)(param);
}
function getAllData() {
return Observable.of( {error:null, data:[333]});
}
function updateData(updateData) {
let param = updateRequest.param;
return createData(1 == param)
.catch((err) => {
err.errorStep = 'CREATE_DATA';
err.hard = true;
throw (err);
}).flatMap(
() => {
return associateData(2 == param)
.catch((err) => {
err.errorStep = 'ASSOCIATE_DATA';
err.hard = true;
return removeData(3==param)
.map(() => {
throw (err);
});
});
},
(createDataResponse, associateDataResponse) => {
return [createDataResponse, associateDataResponse];
}
)
.flatMap(() => {
if (updateData.markAssociationSuccess) {
return markAssociateSuccess(4 == param);
} else {
return Observable.of({});
}
}).catch((err) => {
if (err.hard) {
throw (err);
}
err.errorStep = 'ASSOCIATE_SUCCESS';
return Observable.of({ error: err });
})
.flatMap((data) => {
if (data.error) {
return Observable.of(data);
}
return removeOldData(5 == param);
}).catch((err) => {
if (err.hard) {
throw (err);
}
err.errorStep = 'REMOVE_OLD_DATA';
return Observable.of({ error: err });
})
.flatMap(
function fetchData() {
return getAllData();
},
function resultSelector(prevRes, { error, data }) {
if (error) {
return {};
}
if (prevRes.error) {
data.error = prevRes.error;
}
return data;
}
)
.subscribe(
function onNext(data) {
console.log("Successful operation final data: " + data);
},
function onError(err) {
console.log("Errored out" + JSON.stringify(err));
},
function onComplete() {
console.log("Stream got completed");
}
);
}
const updateRequest = {
param:1,
markAssociationSuccess: true
}
updateData(updateRequest);

Related

Object is possibly undefined in TypeScript

I did use sfDoc !== undefined, but still I'm getting the error of object is possibly undefined. Am I doing anything wrong here?
return database.runTransaction(function (transaction) {
return transaction.get(sfDocRef).then(sfDoc => {
if (!sfDoc.exists) {
throw "Document does not exist!";
}
if (sfDoc !== undefined) {
var usedCount = sfDoc.data().usedCount + 1;
transaction.update(sfDocRef, { usedCount: usedCount });
}
return transaction;
});
}).then(function () {
console.log("Tag field changed!");
return true;
}).catch(function (error) {
console.log("Error in changing Tag field: ", error);
return false;
});
Try this example. Check for the sfDoc and return transaction.update, So that then wait to resolve the promise. According to document, you don not has to check for sfDoc. It will be always defined.
return database
.runTransaction(function (transaction) {
return transaction.get(sfDocRef).then((sfDoc) => {
if (sfDoc && sfDoc.exists) {
var usedCount = sfDoc.data().usedCount + 1;
return transaction.update(sfDocRef, { usedCount: usedCount });
} else {
throw "Document does not exist!";
}
});
})
.then(function () {
console.log("Tag field changed!");
return true;
})
.catch(function (error) {
console.log("Error in changing Tag field: ", error);
return false;
});

wait for asynchronous functions to finish in Angular

So I was working on a new component in Angular and in the ngOninit I have the following asynchronous functions below...
This.getUserProfile needs to be finished before I can call this.getPrivateGroup() and this.getPrivateGroup() needs to be finished before I can call this.loadGroupPosts(). I know I could write these functions inside the callback of the asynchronous requests, but I was wondering if there is a way to keep it in ngOnInit to keep it cleaner?
Anyone has an idea?
ngOnInit() {
this.getUserProfile();
// my-workplace depends on a private group and we need to fetch that group and edit
// the group data before we proceed and get the group post
if (this.isItMyWorkplace) {
this.getPrivateGroup();
}
this.loadGroupPosts();
}
getUserProfile() {
this._userService.getUser()
.subscribe((res) => {
this.user = res.user;
console.log('log user', this.user);
this.profileImage = res.user['profile_pic'];
this.profileImage = this.BASE_URL + `/uploads/${this.profileImage}`;
}, (err) => {
this.alert.class = 'alert alert-danger';
if (err.status === 401) {
this.alert.message = err.error.message;
setTimeout(() => {
localStorage.clear();
this._router.navigate(['']);
}, 3000);
} else if (err.status) {
this.alert.class = err.error.message;
} else {
this.alert.message = 'Error! either server is down or no internet connection';
}
});
}
getPrivateGroup() {
console.log('user check', this.user);
this.groupService.getPrivateGroup(`${this.user.first_name}${this.user.last_name}`)
.subscribe((group) => {
console.log('received response', group)
})
}
// !--LOAD ALL THE GROUP POSTS ON INIT--! //
loadGroupPosts() {
this.isLoading$.next(true);
this.postService.getGroupPosts(this.group_id)
.subscribe((res) => {
// console.log('Group posts:', res);
this.posts = res['posts'];
console.log('Group posts:', this.posts);
this.isLoading$.next(false);
this.show_new_posts_badge = 0;
}, (err) => {
swal("Error!", "Error while retrieving the posts " + err, "danger");
});
}
// !--LOAD ALL THE GROUP POSTS ON INIT--! //
You can use basic promises with async/await.
async ngOnInit() {
await this.getUserProfile(); // <-- 1. change
// my-workplace depends on a private group and we need to fetch that group and edit
// the group data before we proceed and get the group post
if (this.isItMyWorkplace) {
this.getPrivateGroup();
}
this.loadGroupPosts();
}
async getUserProfile() {
this._userService.getUser()
.subscribe((res) => {
this.user = res.user;
console.log('log user', this.user);
this.profileImage = res.user['profile_pic'];
this.profileImage = this.BASE_URL + `/uploads/${this.profileImage}`;
return true; // <-- this
}, (err) => {
this.alert.class = 'alert alert-danger';
if (err.status === 401) {
this.alert.message = err.error.message;
setTimeout(() => {
localStorage.clear();
this._router.navigate(['']);
}, 3000);
} else if (err.status) {
this.alert.class = err.error.message;
} else {
this.alert.message = 'Error! either server is down or no internet connection';
}
throw err;
});
}
You could instead leverage RxJS and use a switchMap something like this (syntax NOT checked):
getData(): Observable<string[]> {
return this._userService.getUser()
.pipe(
switchMap(userInfo=> {
return this.getPrivateGroup();
}),
catchError(this.someErrorHandler)
);
}
One way to do is, return the Observable instead of subscribing in the getPrivateGroup()
getPrivateGroup() {
console.log('user check', this.user);
return this.groupService.getPrivateGroup(`${this.user.first_name}${this.user.last_name}`)
}
And then, subscribe to the data where you want the chain the this.loadGroupPosts()
if (this.isItMyWorkplace) {
this.getPrivateGroup().subscribe(group => {
this.group = group; //you probably want to assign the group data
this.loadGroupPosts()});
}
you could also use the 3rd part of your subscribe function when its completed
i am not quite sure if this is a clean solution, in my opinion it is.
ngOnInit() {
this.getUserProfile();
}
getUserProfile() {
this._userService.getUser()
.subscribe((res) => {
this.user = res.user;
console.log('log user', this.user);
this.profileImage = res.user['profile_pic'];
this.profileImage = this.BASE_URL + `/uploads/${this.profileImage}`;
}, (err) => {
this.alert.class = 'alert alert-danger';
if (err.status === 401) {
this.alert.message = err.error.message;
setTimeout(() => {
localStorage.clear();
this._router.navigate(['']);
}, 3000);
} else if (err.status) {
this.alert.class = err.error.message;
} else {
this.alert.message = 'Error! either server is down or no internet connection';
}
}, () => {
// my-workplace depends on a private group and we need to fetch that group and edit
// the group data before we proceed and get the group post
if (this.isItMyWorkplace) {
this.getPrivateGroup();
}
});
}
getPrivateGroup() {
console.log('user check', this.user);
this.groupService.getPrivateGroup(`${this.user.first_name}${this.user.last_name}`)
.subscribe((group) => {
console.log('received response', group)
}, error => {
console.log(error)
}, () => {
this.loadGroupPosts();
})
}
loadGroupPosts() {
this.isLoading$.next(true);
this.postService.getGroupPosts(this.group_id)
.subscribe((res) => {
// console.log('Group posts:', res);
this.posts = res['posts'];
console.log('Group posts:', this.posts);
this.isLoading$.next(false);
this.show_new_posts_badge = 0;
}, (err) => {
swal("Error!", "Error while retrieving the posts " + err, "danger");
});
}

(...).then is not a function

I need to fix the error on code below, can someone help me please?. The code is written on JS(NodeJs). The error is this (...).then is not a function. Thanks.
try {
var decoded = jwt.decode(JWTToken, { complete: true });
var audience = decoded.payload.aud;
return db.checkAudience(audience).then(ehClient => {
if (ehClient == true) {
return db.getCreditsGeneral(sgecode, collections, year).then(total => {
let result = [];
total.forEach(item => {
result.push({
collection: item.Sistema,
levelType: item.idNivelEnsino,
grade: item.codPortal
});
});
return result;
});
} else {
return "not authorized";
}
});
} catch (err) {
return { erro: err.message };
}

how to let a function wait for result returned by another function?

i have a function called 'updateProfile()' which has a condition which is if(emailChangeConfirm), this condition depends upon the value of variable 'emailChangeConfirm' , this variable gets the value returned by another function called 'updateEmailAllProcessing()'
the condition 'if(emailChangeConfirm)' is not getting satisfied at all because compiler is not waiting for the function 'updateEmailAllProcessing()' to return the value for variable 'emailChangeConfirm'.
I used async/await for this but that is also not working as i want
Desired Solution :
function 'updateProfile()' must wait for the function 'updateEmailAllProcessing()' to get the result in 'emailChangeConfirm' so that i can enter in the condition 'if(emailChangeConfirm)'.
I am using typescript and working on hybrid app with ionic 3 and angular 5.
async updateProfile(updatedData : Credentials,tarUser : Credentials)
{
// console.log(tarUser,'<<--->>>',updatedData)
let count : number = undefined;
let emailChangeConfirm : boolean;
if(updatedData.name)
{
if(tarUser.name != updatedData.name)
tarUser.name = updatedData.name;
else
count++;
}
if(updatedData.email)
{
if(tarUser.email != updatedData.email)
{
**emailChangeConfirm = await this.updateEmailAllProcessing();**
console.log(emailChangeConfirm)
**if(emailChangeConfirm)
tarUser.email = updatedData.email;**
}
else
count++;
}
if(updatedData.phoneNo)
{
if(tarUser.phoneNo != updatedData.phoneNo)
tarUser.phoneNo = updatedData.phoneNo;
else
count++;
}
if(updatedData.photoURL)
{
if(tarUser.photoURL != updatedData.photoURL)
tarUser.photoURL = updatedData.photoURL;
else
count++;
}
if(count)
this.mesService.presentToast('Nothing Updated!!')
else **if(emailChangeConfirm)**
{
this.dbServe.editUser(tarUser).then(() =>
{
console.log("User Edited Successfully with email change too");
this.authServ.updateEmail(tarUser.email).then(() =>
{
console.log('login email updated');
this.authServ.logout();
})
//this.close();
})
}
else
{
this.dbServe.editUser(tarUser).then(() =>
{
console.log("User Edited Successfully with No email change");
this.close();
})
}
}
**async updateEmailAllProcessing()**
{
let result : boolean;
let alert = this.mesService.emailChangeConfirmation();
alert.present();
alert.onDidDismiss((data) => {
console.log('data->',data);
if(data)
{
let alert1 = this.mesService.passwordPrompt();
alert1.present();
alert1.onDidDismiss(data1 =>
{
console.log('password->',data1);
if(data1)
{
this.authServ.reauthenticateUser(data1).then(() =>
{
console.log('User Reauth Done');
result = true;
})
}
else
result = false;
})
}
else
result = false;
})
**return result;**
}
You need updateEmailAllProcessing to return a promise so you can await on it. and resolve the promise with the result inside the callback.
async updateEmailAllProcessing()
{
return new Promise((resolve, reject) => {
let result: boolean;
let alert = this.mesService.emailChangeConfirmation();
alert.present();
alert.onDidDismiss((data) => {
console.log('data->', data);
if (data) {
let alert1 = this.mesService.passwordPrompt();
alert1.present();
alert1.onDidDismiss(data1 => {
console.log('password->', data1);
if (data1) {
this.authServ.reauthenticateUser(data1).then(() => {
console.log('User Reauth Done');
resolve(true);
})
}
else
resolve(false);
})
}
else
resolve(false);
})
});
}

Promise resolution

I am new to javascript promises, I am using below code from NativeScript to query sqlite database and just want to return true if row exists and false otherwise:
function hasBookmark(url) {
(new Sqlite("pakjobs.db")).then(db => {
db.all("SELECT url FROM bookmarks WHERE url=?", [url]).then(rows => {
return rows.length ? true : false;
}, error => {
console.log("hasBookmark ERROR", error);
return false;
});
}, error => {
console.log("hasBookmark ERROR", error);
return false;
});
return false;
}
However function ALWAYS returns false.
Can anybody tell how do I return true if row exists and false otherwise ?
Thanks for the help
You are not going to be able to return true or false, that's pretty much the point of Promises, you can't return something asynchronously in javascript.
What you can do, however, is directly returning the Promise you're creating and then calling .then on it.
function hasBookmark(url) {
return (new Sqlite("pakjobs.db")).then(db => {
return db.all("SELECT url FROM bookmarks WHERE url=?", [url]).then(rows => {
return rows.length ? true : false;
}, error => {
console.log("hasBookmark ERROR", error);
return false;
});
}, error => {
console.log("hasBookmark ERROR", error);
return false;
});
}
const hasBookmarkPromise = hasBookmark('someurl');
hasBookmarPromise.then(value => {
if (value === true) {
console.log('has bookmarks');
}
});
function hasBookmark(url) {
return new Promise((resolve, reject) => {
(new Sqlite("pakjobs.db")).then(db => {
db.all("SELECT url FROM bookmarks WHERE url=?", [url]).then(rows => {
resolve(rows.length ? true : false)
}, error => {
console.log("hasBookmark ERROR", error);
resolve(false);
});
}, error => {
console.log("hasBookmark ERROR", error);
resolve(false);
});
});
}
hasBookmark(url).then(function(isValid){
If(isValid){
// Do something
}
});

Categories