angular 5 httpClient and Promises - javascript

I've been looking at Angular 5's GET POST etc:
get() {
return this.httpClient.get<any>('https://api.github.com/users/seeschweiler');
}
or
http.get<ItemsResponse>('/api/items')
.subscribe(
// Successful responses call the first callback.
data => {...},
// Errors will call this callback instead:
err => {
console.log('Something went wrong!');
});
I don't see that promises are usually used with it.
Is this because it's not really needed or some other reason?

Angular by defaults uses Observables. Observables give you more flexibility working with streams.
If you want to work with Promises you can still cast Observable into Promises by using toPromise function.

Related

NestJs async httpService call

How can I use Async/Await on HttpService using NestJs?
The below code doesn`t works:
async create(data) {
return await this.httpService.post(url, data);
}
The HttpModule uses Observable not Promise which doesn't work with async/await. All HttpService methods return Observable<AxiosResponse<T>>.
So you can either transform it to a Promise and then use await when calling it or just return the Observable and let the caller handle it.
create(data): Promise<AxiosResponse> {
return this.httpService.post(url, data).toPromise();
^^^^^^^^^^^^^
}
Note that return await is almost (with the exception of try catch) always redundant.
Update 2022
toPromise is deprecated. Instead, you can use firstValueFrom:
import { firstValueFrom } from 'rxjs';
// ...
return firstValueFrom(this.httpService.post(url, data))
As toPromise() is being deprecated, you can replace it with firstValueFrom or lastValueFrom
For example:
const resp = await firstValueFrom(this.http.post(`http://localhost:3000/myApi`)
https://rxjs.dev/deprecations/to-promise
rxjs library is most powerful concurrency package that chosen form handling system event like click, external request like get data or delete record and ....
The main concept behind this library is:
handle data that receive in future
therefor you most use 3 argument in observable object like
observablSource.subscribe(
data => { ... },
failure => { ... },
compelete => { ... }
)
but for most backend developer use Promises that comes from ECMAScript 6 feature and is native part of JavaScript.
By default in Angular 4+ and Nest.js use rxjs that support Observable. In technical details you can find a solution for change automatic observable to promise.
const data: Observable<any>;
data.from([
{
id: 1,
name: 'mahdi'
},
{
id: 2,
name: 'reza'
},
])
now you have simulate a request with observable type from server. if you want to convert it to Pormise use chained method like example:
data.toPromise();
from this step you have promised object and form using it better to attach async/await
async userList( URL: string | URLPattern ) {
const userList = await this.http.get<any>( URL ).toPromise();
...
}
try these below one instead of only async-await.
There are some basic reasons behind the deprecation of toPromise.
We know that Promise and Observables that both collections may produce values over time.
where Observables return none or more and Promise return only one value when resolve successfully.
there is a main reason behind the seen
Official: https://rxjs.dev/deprecations/to-promise
Stack:Rxjs toPromise() deprecated
Now we have 2 new functions.
lastValueFrom : last value that has arrived when the Observable completes for more https://rxjs.dev/api/index/function/firstValueFrom
firstValueFrom: you might want to take the first value as it arrives without waiting an Observable to complete for more https://rxjs.dev/api/index/function/lastValueFrom
Following is complete example for working code:
.toPromise() is actually missing
async getAuthToken() {
const payload = {
"SCOPE": this.configService.get<string>('SCOPE'),
"EMAIL_ID": this.configService.get<string>('EMAIL_ID'),
"PASSWORD": this.configService.get<string>('PASSWORD'),
};
const url = this.configService.get<string>('AUTHTOKEN_URL')
const response = await this.httpService.post(
url,
payload
).toPromise();
console.log(response.data);
return response.data;
}
You can just add the .toPromise() at the end of each method call but then you lose the power of observables, like it’s ability to add retry to failed http call by just adding the retry operator.
You can implement this abilities by yourself and create your own module or just use package that already implemented it like this : https://www.npmjs.com/package/nestjs-http-promise

Angular2 Can't get data from response using promise?

I'm trying to get data out of response but I can't seem to get the result I want.
facebookLogin(): void {
this.fb.login()
.then((res: LoginResponse) => {
this.accessToken = res.authResponse.accessToken;
this.expiresIn = res.authResponse.expiresIn;
this.signedRequest = res.authResponse.signedRequest;
this.userId = res.authResponse.userID;
console.log('Logged In', res, this.accessToken); //works without problem
this.router.navigate(['../other-register']);
})
.catch(this.handleError);
console.log(this.accessToken) //printing 'undefined'
}
Here within then => { }, console.log seems to print the data in res without any problem. I can see data I want but when I console.log outside of then =>{ }, it's giving me undefined.
what am I doing wrong? I need to use data inside response and pass them to other component but I can't seem to figure out what I'm doing wrong.
Can anyone help me? Thanks
This is the expected behavior actually.
this.fb.login() is a Promise. This means that the value of the result/response (res) will not readily be available right when it is called, but it 'promises' that it will have a value once some action is taken or a response is returned and 'then' it will do something. In this case that action would be connecting to the Facebook API and having data returned. This is just like Ajax in jQuery if you have experience with that, Promises are a more evolved version of callbacks.
What is happening is that you function is being executed in this order:
this.fb.login() is called. Doesn't have a value yet so it allows the script to continue.
console.log() is called.
this.fb.login's value is returned and the then() closure is executed.
If you want to know when the value is return or perform a specific action once it is returned you can call a function within .then() or look into observables (RxJS) to notify other parts of your application that login was successful (or wasn't).
Observables Example
Here is one example on Observables, however, I would do more research as there are multiple Subjects to select from, all which have slightly different behavior. Also, this kind of pattern works better in Angular2+ if this is performed in a service, that way other components will be able to access the information provided by Facebook.
import { AsyncSubject } from 'rxjs/AsyncSubject';
// ...
response: AsyncSubject<any> = new AsyncSubject();
facebookLogin(): void {
this.fb.login()
.then((res: LoginResponse) => {
this.response.next(res);
this.response.complete();
this.router.navigate(['../other-register']);
})
.catch(this.handleError);
}
You then retrieve the data from within response like this:
this.response.subscribe(result => {
console.log(result);
})
Pass Data Example
Since you already have a function in the service designed to receive the data, this may be a wiser implementation in your case.
facebookLogin(): void {
this.fb.login()
.then((res: LoginResponse) => {
this.user_service.passData(res.authResponse.accessToken);
this.router.navigate(['../other-register']);
})
.catch(this.handleError);
}

Observable determine if subscriber function has finished

What is the best way to determine if the subscriber has finished executing or better yet return something and catch it up-stream? For example:
this._subscriptions.push(this._client
.getCommandStream(this._command) // Returns an IObservable from a Subject stream
.subscribe(msg => {
// Do some processing maybe some promise stuff
http.request(url).then(
// some more stuff
);
});
What's the best know to determine that subscription has finished. I've implemented it as follows:
this._subscriptions.push(this._client
.getCommandStream(this._command)
.subscribe(msg => {
// Do some processing maybe some promise stuff
http.request(url).then(re => {
// some more stuff
msg.done()
}).catch(err => msg.done(err));
});
i.e. added a done method to the object being passed in to determine if this is finished. The issue with that is I'll have to call done in every promise or catch block and find that a little too exhaustive. Is there a cleaner and more automated way of doing this?
I think the examples I've given are not good enough. This implementation is using RX to build an internal messaging bus. The get command stream is actually returning a read-only channel (as an Observable) to get commands and process them. Now the processing could be a http request followed by many other things or just an if statement.
this._client
.getCommandStream(this._command) // Returns an IObservable from a Subject stream
.subscribe(msg => {
// Do some processing maybe some promise stuff
http.request(url).then({
// some more stuff
}).then({
// Here I wanna do some file io
if(x) {
file.read('path', (content) => {
msg.reply(content);
msg.done();
});
} else {
// Or maybe not do a file io or maybe even do some image processing
msg.reply("pong");
msg.done()
}
});
});
I feel like this is a fine usage of the Observable pattern as this is exactly a sequence of commands coming in and this logic would like to act on them. The question is notice msg.done() being called all over the place. I want to know what is the best way to limit that call and know when the entire thing is done. Another option is to wrap it all in a Promise but then again what's the difference between resolve or msg.done()?
Actually, making another asynchronous request inside subscribe() isn't recommended because it just makes things more complicated and using Rx in this way doesn't help you make your code more understandable.
Since you need to make a request to a remote service that returns a PRomise you can merge it into the chain:
this._subscriptions.push(this._client
.getCommandStream(this._command)
.concatMap(msg => http.request(url))
.subscribe(...)
Also the 3rd parameter to subscribe is a callback that is called when the source Observable completes.
You can also add your own teardown logic when the chain is being disposed. This is called after the complete callback in subscribe(...) is called:
const subscription = this._subscriptions.push(this._client
...
.subscribe(...)
subscription.add(() => doWhatever())
Btw, this is equivalent to using the finally() operator.
As per RxJs subscribe method documentation, the last Argument is completed function
var source = Rx.Observable.range(0, 3)
var subscription = source.subscribe(
function (x) {
console.log('Next: %s', x);
},
function (err) {
console.log('Error: %s', err);
},
function () {
console.log('Completed');
});
please refer this documentation
https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/subscribe.md

AngularJS - Do you return the promise or data from the service/factory?

In my controller, I use a method from a factory to update some data. For example, I'm trying to fetch an updated array of users. Should I be returning the promise itself from the factory? Or should I be returning the data from the promise (not sure if I phrased that correctly)?
I ask because I've seen it both ways, and some people say that the controller shouldn't have to handle whether the request was successful or if it failed. I'm specifically talking about the promise returned from $http in this case.
Or maybe in other words, should I be using the then() method inside the factory, or should I be using it in the controller after returning from the factory?
I've tried to handle the success and error callbacks (using the this() method) from within the service, but when I return the data to the controller, the users array is not properly updated. I'm assuming that's because of the request being async. So in the controller, it would look something like this:
vm.users = userFactory.getUsers();
If I handle the promise from within the controller, and set the users array within the then() method, it works fine. But this goes back to where I should be using then():
userFactory.getUsers().then(
function(data){
vm.users = data;
}, ...
Hopefully someone would be able to shed some light on this or provide some input. Thanks!
There's no way you can return the data from the factory (since it's an async call) without using either a callback approach (discouraged):
userFactory.prototype.getUsers = function(callback){
$http.get('users').then(function (response) {
callback(response.data);
});
};
Or the promise approach.
If you're worried about handling the errors on the controller, then worry not! You can handle errors on the service:
userFactory.prototype.getUsers = function(){
return $http.get('users').then(function(response) {
return response.data;
}, function(error) {
// Handle your error here
return [];
});
};
You can return the results of then and it will be chained. So things from service will execute and then, later on, Controller ones.
I have no problem with controller deciding what to do basing on response failing/succeding. In fact it lets you easily handle different cases and doesn't add a lot of overhead to the controller (controller should be as small as possible and focused on current task, but for me going different path whether request failed is the part of its task).
Anyway, in Angular HTTP requests are wrapped in promises internally (I remember that in the previous versions there was a way to automatically unwrap them), so returning from service/factory always returns a promise, which has to be resolved.
I prefer returning a promise from a service/factory because I tend to let other classes decide what to do with the response.

Can gapi.client.load for cloud endpoints return a promise?

A google gapi.client.load for a google api can return a promise as discussed here. However, if you wish to use the javascript client for your own app engine component using cloud endpoints, the gapi.client.load has a different method signature, as shown in this angular post:
gapi.client.load('guestbook', 'v1', function() {
$scope.is_backend_ready = true;
$scope.list();
}, '/_ah/api');
Here, there is a fourth parameter and it is not the success function. So I'm not sure how a promise can be used.
Investigating this further, it seems that if you send "undefined" as the 3rd praameter, then you can use this call as a promise, like:
gapi.client.load('guestbook', 'v1', undefined, '/_ah/api').then(function() {
$scope.is_backend_ready = true;
$scope.list();
});
I haven't seen any google documentation of this. Please let me know if this exists.
If the goal is just to perform some action after the client is loaded you could simply check for an error in the callback function. This will work with or without the client.init function. Something like this:
gapi.client.init({}).then(() => {
gapi.client.load('some-api', "v1", (err) => { callback(err) }, "https://someapi.appspot.com/_ah/api");
}, err, err);
function callback(loadErr) {
if (loadErr) { err(loadErr); return; }
// success code here
}
function err(err){
console.log('Error: ', err);
// fail code here
}
Example
If you really need a promise returned you can simply wrap this code in a function and return a promise in the callback function above.
"Cloud Endpoints client library does not support the Promise API of AngularJS for describing this kind of asynchronous processing."
You can find this stated in https://cloud.google.com/solutions/angularjs-cloud-endpoints-recipe-for-building-modern-web-applications .
To get around this is to write (or better generate) thin wrapper by converting all callbacks to promises
as explained in https://riztaak.wordpress.com/2015/04/05/promises-promises-using-google-cloud-endpoint-in-angularjs/

Categories

Resources