I have two calls that will be based on request it could be single or both api calls at the same time, so i have implemented below code not sure if thats the correct approach to use promise.all , Also want to throw error back to user in case any promise failed.
execute function is executed from the routes.
if there is better way to implement i would appreciate the feedback.
main.ts
public async execute(#Request() request: express.Request): Promise<[any] | any> {
if (request.body.lob === "credit") {
return this.getCardDetails(request);
}
if (request.body.lob === "individual") {
return this.getAccountDetails(request);
}
return Promise.all([this.getCardDetails(request), this.getAccountDetails(request)]);
}
#Post('getAccountDetails')
private async getAccountDetails(#Body() request: any): Promise<any> {
// process retrieveData Call and get response
}
#Post('getCardDetails')
private async getCardDetails(#Body() request: any): Promise<any> {
// process cardDetails Call and get response
}
Related
I have a list of department, which will be fetched from an API link.
When my angular app loads (I means after ngOnInit(): void call), I want :
I will fetch all department as async
Then I will await for all department to load
Finally, I will save 1st department's id in a variable
I am in confusion where I should put my await
(Note: my code is working, API response comes to me, I am only stuck in the await part)
Here is how far I tried:
app.component.ts:
import { Component, OnInit } from '#angular/core';
import { DepartmentModel } from '../../user/models/individual-department.model';
#Component({
selector: 'app-requisition-report-one',
templateUrl: './requisition-report-one.component.html',
styleUrls: ['./requisition-report-one.component.css']
})
export class RequisitionReportOneComponent implements OnInit {
displayIndividualDepartmentList: DepartmentModel[] = [];
currentDisplayDepartment: number = null;
constructor(private userService: UserService) { }
ngOnInit(): void {
this.initializeDepartmentList();
}
initializeDepartmentList() {
this.userService.GetAllIndividualDepartment().subscribe(
async (data: DepartmentModel[]) => {
this.displayIndividualDepartmentList = data;
this.currentDisplayDepartment = await this.displayIndividualDepartmentList[0].department_id;
}
);
}
onShowButtonClick() {
console.log(this.currentDisplayDepartment);
}
}
My vscode is giving me a warning like this
'await' has no effect on the type of this expression.ts(80007)
Besides, on show button click, I am getting null value.
I want to get the first department's id in this variable currentDisplayDepartment from the list displayIndividualDepartmentList after awaiting for the list to be loaded.
TIA
RxJS observables don't rely on async/await feature, but rather on a subscription/callback pattern. Your callback function will be executed everytimes the observable returned by GetAllIndividualDepartment method from UserService emits a new value, and therefore does not require any async prefix. You can access data in a synchronous way with no need to use the await keyword.
initializeDepartmentList() {
this.userService.GetAllIndividualDepartment().subscribe(
(data: DepartmentModel[]) => {
this.displayIndividualDepartmentList = data;
this.currentDisplayDepartment = this.displayIndividualDepartmentList[0].department_id;
}
);
}
A quick explanation of RxJS observables and how they are different from the Promise you are used to work with.
You can convert your Observable to a promise as below and use the async/await syntax as opposed to .then() syntax.
The toPromise() method will handle the subscribe/unsubscribe for you and will only emit when the observable has completed. In the case of HTTP observables they typically only emit one value (primitive or otherwise) and then complete.
You can then use async/await syntax as shown or use .then().
Using toPromise() with HTTP observeables is a very compelling and clean syntax making asynchronous code look synchronous and easier to follow.
async initializeDepartmentList(): Promise<void> {
const data: DepartmentModel[] = await this.userService.GetAllIndividualDepartment().toPromise();
this.displayIndividualDepartmentList = data;
this.currentDisplayDepartment = this.displayIndividualDepartmentList[0].department_id;
}
Call like this...
async ngOnInit(): Promise<void> {
await this.initializeDepartmentList();
.
.
}
Or with a self invoking function like this.
ngOnInit()
{
(async () => {
await this.initializeDepartmentList();
})()
}
Just to add that toPromise is being deprecated in RxJS 7 and will be removed in RxJS 8.
In its place you can use lastValueFrom
const data = await lastValueFrom(this.userService.GetAllIndividualDepartment());
As a side note, if you want to initialise your application upon startup you can use...
app.module.ts
providers: [
{
provide: APP_INITIALIZER,
useFactory: appInitializeService,
multi: true
},
Something like this. A promise returning void. The app effectively waits for your prerequisites to load before continuing.
export const appInitializeService = (): () => Promise<void> => (): Promise<void> => new Promise((resolve) => resolve());
In Javascript, I can wrap a callback as a promise as folllows:
function subscribeAsCallback(request, callback) {
// ... async operation
const response = ...;
callback(response);
}
function subscribeAsPromise(request) {
return new Promise((resolve, reject) => {
subscribeAsCallback(request, (response) => {
resolve(response);
});
});
}
So that I can call it with
subscribeAsPromise(request).then(response => {
// handling response logic
});
And I would like to ask what is the equivalent of it in Java? Here is my code in Java, where I have no idea how to wrap the callback up.
public class DataSubscriber {
public void subscribeAsCallback(Request request, Consumer<Response> consumer) {
// ... async operation
Response response = ...;
consumer.accept(response);
}
public CompletableFuture<Response> subscribeAsPromise(Request request) {
// ... ?
// what is the equivalent of its Java version?
}
}
So that I can call the code as follows:
dataSubscriber.subscribeAsPromise(request).thenApply(response -> {
// handling response logic
});
I just think of a solution using CountDownLatch, though I don't think using CountDownLatch is a good idea, as it just block the thread from execution which wastes some resources. But it will be one possible solution for that.
public class DataSubscriber {
public void subscribeAsCallback(Request request, Consumer<Response> consumer) {
// ... async operation
Response response = ...;
consumer.accept(response);
}
public CompletableFuture<Response> subscribeAsPromise(Request request) {
return CompletableFuture.supplyAsync(() -> {
try {
final Response[] responeResult = { null };
CountDownLatch latch = new CountDownLatch(1);
subscribeAsCallback(request, new Consumer<Response>() {
public void accept(Response response) {
responeResult[0] = response;
latch.countDown();
}
});
latch.await();
return responseResult[0];
} catch (Exception ex) {
throw new RuntimeException(ex);
}
});
}
}
Though the code does not look very nice, it is the first solution I can think of. Hope there are other better answers so that I can learn upon.
You won't be able to pass callback into CompletableFuture. I personally find it easier to handle promises in JavaScript and definitely more verbose in java.
In your example, I don't see the point of passing in the anonymous function (response) => { resolve(response); } to resolve the promise or future since CompletableFuture provides a get() method to resolve the CompletableFuture, which is called after your line of code
CompletableFuture future = dataSubscriber.subscribeAsPromise(request).thenApply(response -> {
// handling response logic
});
// do this to "resolve" the future
future.get();
public class DataSubscriber {
// Please update the name to reflect the correct semantic meaning
public Response subscribeAsCallback(Request request) {
// ... async operation
Response response = ...;
return response;
}
public CompletableFuture<Response> subscribeAsPromise(Request request) {
return CompletableFuture.supplyAsync(this::subscribeAsCallback);
}
}
So in main.ts i am trying to call class method processResponse to get the data back from handler but it always return custObject undefined its nor even stepping into processResponse function , what is implemented wrong in below code ?
main.ts
private async custResponse(data: any): Promise < any > {
const custObject = await RequestResponseHandler.processResponse(data);
return custObject;
}
handler.ts
public static async processResponse(data: any): Promise<any> {
let response: any = {};
console.log("Data>>..>>>>", data); // undefined
try {
if (data.Header.StatusCode === "0000") {
response = data.Details;
const tokenId = await this.cacheResponse(response);
response.Header.tokenID = tokenId;
return response;
}
} catch (err) {
return data;
}
}
Since your console.log("Data>>..>>>>", data); is undefined that means the issue is somewhere upstream. You are not passing anything in the data argument to this method. Try checking where you are calling custResponse method and see if the data is actually being passed or not (Its probably not).
As for the undefined return, In your code you are not returning anything if the status code is not OK (In the try block). Try putting some return at the end.
public static async processResponse(data: any): Promise<any> {
//....
//try catch stuff...
//....
return data //or something else
}
So I am trying to subscribe to a simple service that return data from a local JSON file.
I have managed to get the service working, I can log it out in the function, but when I subscribe to the service in the angular 2 component, it is always undefined. I'm not sure why? Any help would be much appreciated.
API service
export class ApiService {
public data: any;
constructor(private _http: Http) {
}
getData(): any {
return this._http.get('api.json').map((response: Response) => {
console.log('in response', response.json()); //This logs the Object
this.data = response.json();
return this.data;
})
.catch(this.handleError);
}
}
Component
export class AppComponent {
public data: any
public informationData;
constructor(private _api: ApiService) {}
public ngOnInit(): void {
console.log(this.getDataFromService()); // This return undefined
}
public getDataFromService() {
this._api.getData().subscribe(response => {
this.informationData = response;
return this.informationData;
});
}
}
Maybe some pictures help?
The numbers here indicate the order of operations.
Send the Http Request
Component is initialized and calls the getMovies method of the movieService.
The movieService getMovies method returns an Observable. NOT the data at this point.
The component calls subscribe on the returned Observable.
The get request is submitted to the server for processing.
The ngOnInit method is complete.
Any code here after the subscribe cannot access the movies property since the data has not yet been returned.
Receive the Http Response
At some LATER point in time ...
The movies are returned to the service.
If the process was successful, the first callback function is executed.
The local movies property is assigned to the movies returned from the service. It is only here that the movies property is finally set.
Attempting to access the movies property prior to step #8 results in an error.
Can we access the value here? NO
To fix it:
objResponse;
this.service.getData().subscribe((result: any)=> {
this.objResponse=result;
}
Returning something won't required
you can do it like this:
In your app-component:
public getDataFromService() {
this._api.getData(this);
}
public setData(data: any){
this.data=data;
}
In your service/api.ts:
public getData(obj: appComponentModel){
this.http.get(url).subscribe(res => obj.setData(res));
}
Try with:
getData(): any {
return this._http.get('api.json');
}
or
getData(): any {
return this._http.get('api.json').map((response: Response) => {
response.json();
})
You've got a problem between sync and async function. You'r issue is: getDateFromService is syncronous and the content inside is async. So when the ngOnInit function call getDataFromService, you'r code don't wait the async task. you'r getDataFromService need to return an observer or need to implement the return of your API (you need to choose).
public ngOnInit(): void {
console.log(this.getDataFromService().subscribe(data => console.log(data)); // This return undefined
}
public getDataFromService() {
return this._api.getData();
}
Instead of logging at the ngOnInit() method as you did
public ngOnInit(): void {
console.log(this.getDataFromService()); // This return undefined }
log inside the subscribe() method as
export class AppComponent {
public data: any
public informationData;
constructor(private _api: ApiService) {}
public ngOnInit(): void {
this.getDataFromService(); //don't log here, logging here will return undefined
}
public getDataFromService() {
this._api.getData().subscribe(response => {
this.informationData = response;
console.log(this.informationData); //log here, like this
return this.informationData;
});
}
}
Imagine 'subscribe' as a separate thread running, write everything that is needed inside an anonymous function inside 'subscribe'. Whenever the 'data' is available, it will be available inside the subscribe method.
Hope this helps.
I have service which returns an observable which does an http request to my server and gets the data. I want to use this data but I always end up getting undefined. What's the problem?
Service:
#Injectable()
export class EventService {
constructor(private http: Http) { }
getEventList(): Observable<any>{
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this.http.get("http://localhost:9999/events/get", options)
.map((res)=> res.json())
.catch((err)=> err)
}
}
Component:
#Component({...})
export class EventComponent {
myEvents: any;
constructor( private es: EventService ) { }
ngOnInit(){
this.es.getEventList()
.subscribe((response)=>{
this.myEvents = response;
});
console.log(this.myEvents); //This prints undefined!
}
}
I checked How do I return the response from an asynchronous call? post but couldn't find a solution
Reason:
The reason that it's undefined is that you are making an asynchronous operation. Meaning it'll take some time to complete the getEventList method (depending mostly on your network speed).
So lets look at the http call.
this.es.getEventList()
After you actually make ("fire") your http request with subscribe you will be waiting for the response. While waiting, javascript will execute the lines below this code and if it encounters synchronous assignments/operations it'll execute them immediately.
So after subscribing to the getEventList() and waiting for the response,
console.log(this.myEvents);
line will be executed immediately. And the value of it is undefined before the response arrives from the server (or to whatever that you have initialized it in the first place).
It is similar to doing:
ngOnInit(){
setTimeout(()=>{
this.myEvents = response;
}, 5000);
console.log(this.myEvents); //This prints undefined!
}
**Solution:**
>So how do we overcome this problem? We will use the callback function which is the `subscribe` method. Because when the data arrives from the server it'll be inside the `subscribe` with the response.
So changing the code to:
this.es.getEventList()
.subscribe((response)=>{
this.myEvents = response;
console.log(this.myEvents); //<-- not undefined anymore
});
will print the response.. after some time.
**What you should do:**
There might be lots of things to do with your response other than just logging it; you should do all these operations inside the callback (inside the subscribe function), when the data arrives.
Another thing to mention is that if you come from a Promise background, the then callback corresponds to subscribe with observables.
**What you shouldn't do:**
You shouldn't try to change an async operation to a sync operation (not that you can). One of the reasons that we have async operations is to not make the user wait for an operation to complete while they can do other things in that time period. Suppose that one of your async operations takes 3 minutes to complete, if we didn't have the async operations then the interface would freeze for 3 minutes.
Suggested Reading:
The original credit to this answer goes to: How do I return the response from an asynchronous call?
But with the angular2 release we were introduced to typescript and observables so this answer hopefully covers the basics of handling an asynchronous request with observables.
Making a http call in angular/javascript is asynchronous operation.
So when you make http call it will assign new thread to finish this call and start execution next line with another thread.
That is why you are getting undefined value.
so make below change to resolve this
this.es.getEventList()
.subscribe((response)=>{
this.myEvents = response;
console.log(this.myEvents); //<-this become synchronous now
});
You can use asyncPipe if you use myEvents only in template.
Here example with asyncPipe and Angular4 HttpClient example
Observables are lazy so you have to subscribe to get the value. You subscribed it properly in your code but simultaneously logged the output outside the 'subscribe' block. That's why it is 'undefined'.
ngOnInit() {
this.es.getEventList()
.subscribe((response) => {
this.myEvents = response;
});
console.log(this.myEvents); //Outside the subscribe block 'Undefined'
}
So if you log it inside the subscribe block then it will log response properly.
ngOnInit(){
this.es.getEventList()
.subscribe((response)=>{
this.myEvents = response;
console.log(this.myEvents); //Inside the subscribe block 'http response'
});
}
Here the problem is, you are initializing this.myEvents into subscribe() which is an asynchronous block while you are doing console.log() just out of subscribe() block.
So console.log() getting called before this.myEvents gets initialized.
Please move your console.log() code as well inside subscribe() and you are done.
ngOnInit(){
this.es.getEventList()
.subscribe((response)=>{
this.myEvents = response;
console.log(this.myEvents);
});
}
The result is undefined because angular process async .
you can trying as below:
async ngOnInit(){
const res = await this.es.getEventList();
console.log(JSON.stringify(res));
}
Also make sure that you map your response to a json output. Otherwise it will return plain text. You do it this like this:
getEventList(): Observable<any> {
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this.http.get("http://localhost:9999/events/get", options)
.map((res)=>{ return res.json();}) <!-- add call to json here
.catch((err)=>{return err;})
}
Undefined because the value here is logged before any data from the service is set from that above subscribe service call. So you have to wait until the ajax call finishes and set the data from the response data.
getEventList(): Observable<any>{
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this.http.get("http://localhost:9999/events/get", options)
.map((res)=> res.json())
.catch((err)=> err)
}
Here make the Console log inside the subscribe method that will make the log when the data is set in myEvents variable.
ngOnInit(){
this.es.getEventList()
.subscribe((response)=>{
this.myEvents = response;
// This prints the value from the response
console.log(this.myEvents)
});
}
To do this you have 2 options:
Suppose we have a service which is returning shipping details array :
getShippingPrices(): Observable<IShippingDetails[]> {
return this.http.get<IShippingDetails[]>('/assets/shipping.json');
}
1. Use Async pipe : Easy way when you just want to show the result in template
In the component class directly assign the observable to variable:
export class ShippingComponent implements OnInit {
shipOptions1 = this.cartService.getShippingPrices();
constructor(private cartService: CartService) {}
ngOnInit() {}
}
and then use async pipe in template :
<div *ngFor="let s of shipOptions1 |async">
<label>{{s.type}}</label>
</div>
Refer: Check the 4th point in this URL
https://angular.io/start/start-data#configuring-the-shippingcomponent-to-use-cartservice
2. Use Subscribe : When you want to manipulate it or want do some business logic on/from response
export class ShippingComponent implements OnInit {
shipOptions2: IShippingDetails[] = [];
constructor(private cartService: CartService) {}
ngOnInit() {
this.cartService.getShippingPrices().subscribe(response => {
this.shipOptions2 = response;
//console.log(this.myEvents);
//All other code using shipOptions2
});
}
}
You can simply try this method-
let headers = new Headers({'Accept': 'application/json'});
let options = new RequestOptions({headers: headers});
return this.http
.get(this.yourSearchUrlHere, options) // the URL which you have defined
.map((res) => {
res.json(); // using return res.json() will throw error
}
.catch(err) => {
console.error('error');
}