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
Related
I have the following endpoint in a class called UserApi.js:
const controller = 'User';
...
export async function getEmployeeInfo(employeeId)
{
const query = createQueryFromObject({employeId});
const response = await get(`/${controller}/EmployeeInfo?${query}`);
return retrieveResponseData(response, []);
}
This is going to get the required information from an action method in the backend of UserController.cs.
Now, say that I want to display this information in EmployeeView.vue class, do I have to await it again? Why or why not? Initially, I would say no, you don't, as you already dealt with the await/async in the UserApi.js class, but what about the Promise.resolve? Please explain.
methods: {
async setReportData(
employeeId
) {
this.isBusy = true;
Promise.resolve(getEmployeeInfo(
employeeId
)).then((resultsEmployeeInfo) => {
this.reportDatatableEmployeeInfo = resultsEmployeeInfo;
})
.catch(() => {
this.alerts.error('An error has occurred while fetching the data');
})
.finally(() => {
this.isBusy = false;
});
},
Update:
....
* #param {Object} response
* #param {any} defaultData
* #param {Function} predicate
* #returns {Promise}
*/
export function retrieveResponseData(response, defaultData = null, predicate = (predicateResponse) => predicateResponse) {
const data = predicate(response) ? response.data : null;
return data || defaultData;
}
You need to await it since a function declared with async keyword ALWAYS returns a Promise, even if you do only synchronous stuff inside of it, hence you need to await or "thenize" it to access the value it resolved to. That depends from the implementation details of async functions which are just generators that yield promises.
If this concerns you because you work inside sync modules and don't like to use async functions just to execute more async functions, there's a good news, TOP-LEVEL await MODULES proposal is at stage 4, so it'll very soon be shipped with the next ECMA version. This way you will be able to await inside modules as if they were wrapped by async functions !
https://github.com/tc39/proposal-top-level-await
I can't tell if you need to await it again, because I can't tell what retrieveResponseData does. It might take the resolved value and wrap it in a fresh promise, which would then require callers of getEmployeeInfo to await the result.
Here's the why:
A Promise is a wrapper around a value
await unwraps a Promise. So does the .then() handler you can register with a Promise (but the value is only unwrapped within the function you provide to .then()).
Just like a gift in the real world, once something has been unwrapped, you don't need to unwrap it again. However, very conveniently for us, you can still use await on a value that is not wrapped in a Promise, and it will just give you the value.
You can wrap any value in a Promise, like so:
let wrappedFive = Promise.resolve(5)
//> wrappedFive is a Promise that must be unwrapped to access the 5 inside it
// this does _exactly_ the same thing as the above
let wrappedFive = new Promise(resolve => {
resolve(5)
})
Sometimes you end up in a situation where you can't use await, because you're in a function that cannot be marked async. The lifecycle methods of front-end frameworks like React (and possibly Vue) are like that: the framework needs each lifecycle method to do its job and be done immediately. If you mark the lifecycle method as async, you can often prevent it from having the intended effect.
In cases like that, you often need to use chained .then() handlers, which is a little uglier, but it works:
componentDidMount() {
// this API call is triggered immediately by lifecycle,
// but it basically starts a separate thread -- the rest
// of this function does not wait for the call to finish
API.getUserInfo()
.then(userInfo => {
// this happens after the API call finishes, but
// componentDidMount has already finished, so what happens
// in here cannot affect that function
this.setState({ username: userInfo.username })
})
// this happens immediately after the API call is triggered,
// even if the call takes 30 seconds
return 5
}
Note that a Promise does not actually start a separate thread -- these all happen in the same thread that executes the lifecycle method, i.e. the browser's renderer thread. But if you think of the codepath that executes, a Promise that you don't wait for basically introduces a fork into that codepath: one path is followed immediately, and the other path is followed whenever the Promise resolves. Since browserland is pretty much a single-threaded context, it doesn't really hurt you to think of creating a Promise as spawning a separate thread. This is a nuance you can ignore until you are comfortable with asychronous patterns in JS.
Update: retrieveResponseData does not appear to return a Promise. I could be wrong, if predict returns a Promise, but if that were true, then the ternary would always return response.data because unwrapped Promises are truthy values (even Promise.resolve(false) is truthy).
However, anyone who calls getEmployeeInfo will have to wait it, because that function is marked async, and that means it returns a Promise even if nothing inside it is is asynchronous. Consider this extreme example:
// this function returns a number
function gimmeFive() {
return 5
}
// this function returns a Promise wrapped around a number
async function gimmeFive() {
return 5
}
Async function getEmployeeInfo awaits the result of the get call in order to return the value returned by a call to retrieveResponeData.
Assuming neither get nor retrieveResponeData errors, the value syntactically returned in the body of getEmployeeInfo is used to resolve the promise object actually returned by calling getEmployeeInfo.
Promise.resolve is not needed to convert the result of calling getEmployeeInfo into a promise because, given async functions return promises, it already is.
It doesn't matter if retrieveResponseData returns a promise or not: standard async function processing waits for a returned promise value to be settled before resolving the promise returned when calling the async function.
Async function setRreportData is declared as async but written using chained promise handler methods to process data and error conditions in order - the async declaration could be omitted, but may be useful if modifications are made.
The results can only be used to update the page at a time when the data has finished being obtained and extracted, shown as a comment in the following code:
setReportData( employeeId) {
this.isBusy = true;
getEmployeeInfo(
employeeId
).then((resultsEmployeeInfo) => {
this.reportDatatableEmployeeInfo = resultsEmployeeInfo;
// At this point the result in this.reportDatatableEmployeeInfo can be used to update the page
})
.catch(() => {
this.alerts.error('An error has occurred while fetching the data');
})
.finally(() => {
this.isBusy = false;
});
},
Displaying the data using EmployeeView.vue class must wait until the data is available. The simplest place to insert updating the page (in the posted code) is within the then handler function inside setReportData.
Displaying the data
As posted setReportData does not notify its caller of when report data is available, either by means of a callback or by returning a promise. All it does is save the result in this.reportDatatableEmployeeInfo and dies.
Without using callbacks, setReportData is left with two choices
Return a promise that is fulfilled with, say,resultsEmployeeInfo or undefined if an error occurs:
setReportData( employeeId) {
this.isBusy = true;
// return the final promise:
return getEmployeeInfo(
employeeId
)
.then( (resultsEmployeeInfo) =>
(this.reportDatatableEmployeeInfo = resultsEmployeeInfo)
)
.catch(() => {
this.alerts.error('An error has occurred while fetching the data');
// return undefined
})
.finally(() => {
this.isBusy = false;
});
},
which could be used in a calling sequence using promises similar to
if(!this.busy) {
this.setReportData(someId).then( data => {
if( data) {
// update page
}
}
If you wanted to make the call in an async context you could use await:
if(!this.busy) {
const data = await this.setReportData(someId);
if( data) {
// update page
}
}
Update the page from within setReportData after the data becomes available ( as shown as a comment in the first part of this answer). The method should probably be renamed from setReportData to getReportData or similar to reflect its purpose.
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.
Question:
Is there an "easy" way to cancel ($q-/$http-)promises in AngularJS or determine the order in which promises were resolved?
Example
I have a long running calculation and i request the result via $http. Some actions or events require me to restart the calculation (and thus sending a new $http request) before the initial promise is resolved. Thus i imagine i can't use a simple implementation like
$http.post().then(function(){
//apply data to view
})
because I can't ensure that the responses come back in the order in which i did send the requests - after all i want to show the result of the latest calculation when all promises were resolved properly.
However I would like to avoid waiting for the first response until i send a new request like this:
const timeExpensiveCalculation = function(){
return $http.post().then(function(response){
if (isNewCalculationChained) {return timeExpensiveCalculation();}
else {return response.data;}
})
}
Thoughts:
When using $http i can access the config-object on the response to use some timestamps or other identifiers to manually order the incoming responses. However i was hoping I could just tell angular somehow to cancel an outdated promise and thus not run the .then() function when it gets resolved.
This does not work without manual implementation for $q-promises instead of $http though.
Maybe just rejecting the promise right away is the way to go? But in both cases it might take forever until finally a promise is resolved before the next request is generated (which leads to an empty view in the meantime).
Is there some angular API-Function that i am missing or are there robust design patterns or "tricks" with promise chaining or $q.all to handle multiple promises that return the "same" data?
I do it by generating a requestId, and in the promise's then() function I check if the response is coming from the most recent requestId.
While this approach does not actually cancel the previous promises, it does provide a quick and easy way to ensure that you are handling the most recent request's response.
Something like:
var activeRequest;
function doRequest(params){
// requestId is the id for the request being made in this function call
var requestId = angular.toJson(params); // I usually md5 hash this
// activeRequest will always be the last requestId sent out
activeRequest = requestId;
$http.get('/api/something', {data: params})
.then(function(res){
if(activeRequest == requestId){
// this is the response for last request
// activeRequest is now handled, so clear it out
activeRequest = undefined;
}
else {
// response from previous request (typically gets ignored)
}
});
}
Edit:
On a side-note, I wanted to add that this concept of tracking requestId's can also be applied to preventing duplicate requests. For example, in my Data service's load(module, id) method, I do a little process like this:
generate the requestId based on the URL + parameters.
check in requests hash-table for the requestId
if requestId is not found: generate new request and store promise in hash-table
if requestId is found: simply return the promise from the hash-table
When the request finishes, remove the requestId's entry from the hash-table.
Cancelling a promise is just making it not invoke the onFulfilled and onRejected functions at the then stage. So as #user2263572 mentioned it's always best to let go the promise not cancelled (ES6 native promises can not be cancelled anyways) and handle this condition within it's then stage (like disregarding the task if a global variable is set to 2 as shown in the following snippet) and i am sure you can find tons of other ways to do it. One example could be;
Sorry that i use v (looks like check character) for resolve and x (obvious) for reject functions.
var prom1 = new Promise((v,x) => setTimeout(v.bind(null,"You shall not read this"),2000)),
prom2,
validPromise = 1;
prom1.then(val => validPromise === 1 && console.log(val));
// oh what have i done..!?! Now i have to fire a new promise
prom2 = new Promise((v,x) => setTimeout(v.bind(null,"This is what you will see"),3000));
validPromise = 2;
prom2.then(val => validPromise === 2 && console.log(val));
I'm still trying to figure out a good way to unit test this, but you could try out this kind of strategy:
var canceller = $q.defer();
service.sendCalculationRequest = function () {
canceller.resolve();
return $http({
method: 'GET',
url: '/do-calculation',
timeout: canceller.promise
});
};
In ECMA6 promises, there is a Promise.race(promiseArray) method. This takes an array of promises as its argument, and returns a single promise. The first promise to resolve in the array will hand off its resolved value to the .then of the returned promise, while the other array promises that came in second, etc., will not be waited upon.
Example:
var httpCall1 = $http.get('/api/something', {data: params})
.then(function(val) {
return {
id: "httpCall1"
val: val
}
})
var httpCall2 = $http.get('/api/something-else', {data: params})
.then(function(val) {
return {
id: "httpCall2"
val: val
}
})
// Might want to make a reusable function out of the above two, if you use this in Production
Promise.race([httpCall1, httpCall2])
.then(function(winningPromise) {
console.log('And the winner is ' + winningPromise.id);
doSomethingWith(winningPromise.val);
});
You could either use this with a Promise polyfil, or look into the q.race that someone's developed for Angular (though I haven't tested it).
I have a modal popup, which I am coding such that when an open() function is called, it returns an observable. Any subscriber will then get an object with a property indicating what button was pressed in the modal or whether it was closed, etc.
If a 'success' button is pressed, I want to make a http call, which in turn also returns an observable! How would I combine these two observables? In angular 1 with promises I can return promises from promises, so I would do something like
var promise = modal.open()
.then(function(res) {
if (res.success) {
return httpService.get(); // also returns a promise
}
return res;
});
how would I do something like this for observables?
You can leverage observable operators to build an asynchronous data flow. In you case, the switchMap operator:
var observable = modalObservable.switchMap(() => {
return return httpService.get(); // also returns an observable
});
Be careful to import the operators you need since they aren't by default (see this question: Angular 2 HTTP GET with TypeScript error http.get(...).map is not a function in [null]):
import 'rxjs/add/operator/switchMap';
You could have a look at the following article and presentation for more details:
The introduction to Reactive Programming you've been missing - https://gist.github.com/staltz/868e7e9bc2a7b8c1f754
Everything is a stream - http://slides.com/robwormald/everything-is-a-stream
I'd like to be able to await on an observable, e.g.
const source = Rx.Observable.create(/* ... */)
//...
await source;
A naive attempt results in the await resolving immediately and not blocking execution
Edit:
The pseudocode for my full intended use case is:
if (condition) {
await observable;
}
// a bunch of other code
I understand that I can move the other code into another separate function and pass it into the subscribe callback, but I'm hoping to be able to avoid that.
You have to pass a promise to await. Convert the observable's next event to a promise and await that.
if (condition) {
await observable.first().toPromise();
}
Edit note: This answer originally used .take(1) but was changed to use .first() which avoids the issue of the Promise never resolving if the stream ends before a value comes through.
As of RxJS v8, toPromise will be removed. Instead, the above can be replaced with await firstValueFrom(observable)
Use the new firstValueFrom() or lastValueFrom() instead of toPromise(), which as pointed out here, is deprecated starting in RxJS 7, and will be removed in RxJS 8.
import { firstValueFrom} from 'rxjs';
import { lastValueFrom } from 'rxjs';
this.myProp = await firstValueFrom(myObservable$);
this.myProp = await lastValueFrom(myObservable$);
This is available in RxJS 7+
See: https://indepth.dev/rxjs-heads-up-topromise-is-being-deprecated/
It likely has to be
await observable.first().toPromise();
As it was noted in comments before, there is substantial difference between take(1) and first() operators when there is empty completed observable.
Observable.empty().first().toPromise() will result in rejection with EmptyError that can be handled accordingly, because there really was no value.
And Observable.empty().take(1).toPromise() will result in resolution with undefined value.
Edit:
.toPromise() is now deprecated in RxJS 7 (source: https://rxjs.dev/deprecations/to-promise)
New answer:
As a replacement to the deprecated toPromise() method, you should use
one of the two built in static conversion functions firstValueFrom or
lastValueFrom.
Example:
import { interval, lastValueFrom } from 'rxjs';
import { take } from 'rxjs/operators';
async function execute() {
const source$ = interval(2000).pipe(take(10));
const finalNumber = await lastValueFrom(source$);
console.log(`The final number is ${finalNumber}`);
}
execute();
// Expected output:
// "The final number is 9"
Old answer:
If toPromise is deprecated for you, you can use .pipe(take(1)).toPromise but as you can see here it's not deprecated.
So please juste use toPromise (RxJs 6) as said:
//return basic observable
const sample = val => Rx.Observable.of(val).delay(5000);
//convert basic observable to promise
const example = sample('First Example')
.toPromise()
//output: 'First Example'
.then(result => {
console.log('From Promise:', result);
});
async/await example:
//return basic observable
const sample = val => Rx.Observable.of(val).delay(5000);
//convert basic observable to promise
const example = await sample('First Example').toPromise()
// output: 'First Example'
console.log('From Promise:', result);
Read more here.
You will need to await a promise, so you will want to use toPromise(). See this for more details on toPromise().
Using toPromise() is not recommended as it is getting depreciated in RxJs 7 onwards. You can use two new operators present in RxJs 7 lastValueFrom() and firstValueFrom(). More details can be found here
const result = await lastValueFrom(myObservable$);
Implementations in Beta version are available here:
firstValueFrom
lastValueFrom
I am using RxJS V 6.4.0, hence I should use deprecated one in V 7.x.x toPromise(). Inspired by other answers, here is what I did with toPromise()
import { first, ... } from 'rxjs/operators';
...
if (condition) {
await observable$.pipe(first()).toPromise();
}
...
Note how I used last() inside a pipe(). Because on mine observable.first() does not works just like mentioned by macil
Hope this helps others who using RxJS V 6.x.x as I do :).
Thanks.