Angular using promise with generics inside promise - javascript

I need to interlace promises in my app:
protected scroll<T>(path: string, pageSize: number, filter: string, data: T[]): Promise<T[]> {
let promise = new Promise<T[]>(function(resolve, reject) {
this.httpClient
.get<T[]>(this.appConfigService.buildApiUrl(path), { params })
.toPromise<T[]>()
.then(result => {
if (result) {
resolve(data.concat(result));
}
})
.catch(function(e) {
reject(e);
});
});
return promise;
}
My problem is that I receive following message:
'Untyped function calls may not accept type arguments'
How would I solve this?
UPDATE:
I should not have removed the if condition from the example:
if (!filter) {
const params = new HttpParams()
.set('searchText', '')
.set('skip', data.length.toString())
.set('take', pageSize.toString());
const promise = new Promise<T[]>((resolve, reject) => {
this.httpClient
.get<T>(this.appConfigService.buildApiUrl(path), { params })
.toPromise<any>()
.then(result => {
if (result) {
resolve(data.concat(result));
}
resolve(data);
})
.catch(e => reject(e));
});
return promise;
}
// also returning a promise
return this.filter<T>(data, pageSize, filter, path);

There are a couple of problems there.
The error message is because you're using <T[]> on get and toPromise, which aren't generic functions. Just apply the type T to result in the then handler.
You're falling into the promise creation antipattern. You already have a promise (from this.httpClient), so you don't need new Promise.
You're using a traditional function for your new Promise callback, but then using this within it as though it were still referring to your class instance. If you were going to keep the new Promise, you'd want an arrow function instead, so it closes over this.
Instead (see *** comments):
protected scroll<T>(path: string, pageSize: number, filter: string, data: T[]): Promise<T[]> {
// *** Return the result of calling `then` on the promise from `toPromise`
return this.httpClient
// *** Don't use <T[]> on `get` and `toPromise`
.get(this.appConfigService.buildApiUrl(path), { params })
.toPromise()
.then((result: T) => { // *** <== Note the `T`
// *** If it's really possible that `result` will be falsy and you don't want that
// to be valid, you can do this:
if (!result) {
throw new Error("appropriate error here");
}
return data.concat(result);
});
}
On the playground
UPDATE:
I should not have removed the if condition from the example:
It doesn't matter, just put the body of the above into the if block:
protected scroll<T>(path: string, pageSize: number, filter: string, data: T[]): Promise<T[]> {
if (!filter) {
const params = new HttpParams()
.set('searchText', '')
.set('skip', data.length.toString())
.set('take', pageSize.toString());
return this.httpClient
// *** Don't use <T[]> on `get` and `toPromise`
.get(this.appConfigService.buildApiUrl(path), { params })
.toPromise()
.then((result: T) => { // *** <== Note the `T`
// *** If it's really possible that `result` will be falsy and you don't want that
// to be valid, you can do this:
if (!result) {
throw new Error("appropriate error here");
}
return data.concat(result);
});
}
return this.filter<T>(data, pageSize, filter, path);
}

Related

typescript, what is the type of a new Promise object?

In TS, I have a function like:
function a() : FOO {
return new Promise((resolve, reject) => {
//omitted
})
}
What is the proper type to write at FOO?
Is it good to just say Promise<any> ?
Perhaps it's helpful to show more details for what I am returning in function a:
new Promise((resolve, reject) => {
const cmd = spawn(str, null, { shell: true });
cmd.stdout.on("data", (data) => {
//just logging
});
cmd.stderr.on("data", (data) => {
return reject(data);
});
cmd.on("error", (error) => {
return reject(error);
});
cmd.on("close", (code) => {
return resolve(code);
});
});
It depends entirely on what type the fulfillment value of the promise will be. For instance, if the promise will be fulfilled with a number, then the type would be Promise<number>. For example:
function a() : Promise<number> {
return new Promise((resolve, reject) => {
doSomeCallbackBasedThing((err: Error | null, result: number) => {
if (err) {
reject(err);
} else {
resolve(result);
}
});
})
}
If it is fulfilled with undefined via resolve() with no argument, typically you'd write Promise<void>.
Often, you don't have to write the type explicitly at all; TypeScript can often infer it from the code in the function.
Re your edit: You'd use Promise<x> where x is the type of the code parameter you receive in your handler for the close event.

Wrapping Auth0's parseHash function in a Promise

auth0.js has a function that's used to parse the URL hash fragment and extract the authentication result therefrom. I'm wrapping this function within one called loadSession as follows:
public loadSession(): void {
this.auth0.parseHash((err, authResult) => {
if (authResult) {
window.location.hash = '';
localStorage.setItem('token', authResult.accessToken);
// TODO (1)
} else if (err) {
// TODO (2)
}
});
}
As seen above, parseHash takes a callback function as an argument and I cannot control that. I would like loadSession to return a Promise that would be resolved at // TODO (1) and rejected at // TODO (2) above. This way I can do obj.loadSession().then(() => { // do something if successful }).catch((err) => { // raise error if not })
Simply wrap it inside a promise:
public loadSession() {
return new Promise((resolve, reject) => {
this.auth0.parseHash((err, authResult) => {
if(authResult) {
window.location.hash = '';
localStorage.setItem('token', authResult.accessToken);
resolve(authResult);
} else if (err) {
reject(err);
}
});
});
}
You can pretty much pass any callback function to a function that returns a promise given:
The callback is the last argument
The callback function takes error as it's first argument
Here is an example:
const asPromise =
(context) =>
(fn) =>
(args) =>
new Promise(
(resolve,reject) =>
fn.apply(
context,
(args||[]).concat(
function(){
if(arguments[0]){
reject(arguments[0]);return;
}
resolve(Array.from(arguments).slice(1));
}
)
)
);
// to apply parseHash on auth0
public loadSession(): Promise {
return asPromise(this.auth0)(this.auth0.parseHash)()
.then(
([authResult])=>{
if (authResult) {
window.location.hash = '';
localStorage.setItem('token', authResult.accessToken);
//whatever you return here is the resolve
return authResult;
}
//just throw in a promise handler will return a rejected promise
// this is only for native promises, some libraries don't behave
// according to spec so you should test your promise polyfil if supporting IE
throw "Promise resolved but got no authResult";
}
)
}
public loadSession(): Promise {
return new Promise((resolve, reject) => {
this.auth0.parseHash((err, authResult) => {
if (authResult) {
window.location.hash = '';
localStorage.setItem('token', authResult.accessToken);
// TODO (1)
// resolve(something)
} else if (err) {
// TODO (2)
// reject(something)
}
});
}
For more information about using Promise API, you can visit MDN Docs
Or you can use a tiny library that does that for you: promisify-auth0 on GitHub, promisify-auth0 on npmjs.org.
Now updated to version 9.5.1.

Typescript: Type narrowing on promises

I'm trying to figure out how to use typeguards on promises based on parameters.
function request({ logic }: { logic: boolean }) {
return new Promise((resolve, reject) => {
if (l)
resolve("something");
resolve(1);
});
}
request({ logic: true }).then(a => {
a.length
})
In this example, I'd like to make sure that the typeof 'a' == 'string'. I tried writing some typeguards in request but their results get lost. I don't know if this is just a limitation of typescript or I just need to do some smart type casting or what.
This is a toy example of what I am actually trying to do, which is to make an async call whose result varies slightly based on some parameters. And I am loath to make another function just to cover an altered return type
Typescript function overloading to the rescue:
function request(logic: true): Promise<string>;
function request(logic: false): Promise<number>;
function request(logic: boolean) {
return new Promise((resolve, reject) => {
if (logic)
resolve("something");
resolve(1);
});
}
request(true).then(a => {
console.log(a.length); //<-- knows that a is a string
});
request(false).then(a => {
console.log(a.length); //<-- error: prop 'length' does not exist on number
});
Typeguards are meant to be used in if statements.
EDIT
You would be surprised! Typescript supports overloading distinction based on fields too! Check the following code:
function request(opts: { logic: true }): Promise<string>;
function request(opts: { logic: false }): Promise<number>;
function request(opts: { logic: boolean }) {
return new Promise((resolve, reject) => {
if (opts.logic)
resolve("something");
resolve(1);
});
}
request({ logic: true }).then(a => {
console.log(a.length); //<-- knows that a is a string
});
request({ logic: false }).then(a => {
console.log(a.length); //<-- error: prop length cannot be found on type number
});
EDIT
With a little generic magic you can achieve the desired behavior. This way only the logic field matters from the caller's point of view. Downside is that you loose typecheck even for opts.logic inside the request functions implementation.
function request<T extends { logic: true }>(opts: T): Promise<string>;
function request<T extends { logic: false }>(opts: T): Promise<number>;
function request(opts: any) {
return new Promise((resolve, reject) => {
if (opts.logic)
resolve("something");
resolve(1);
console.log(opts.anything);
});
}
request({ logic: true, foo: 'bar' }).then(a => {
console.log(a.length); //<-- knows that a is a string
});
request({ logic: false, foo: 'baz' }).then(a => {
console.log(a.length); //<-- error: prop length cannot be found on type number
});
Correct overloading is the next (should add type):
function request(logic: boolean): Promise<string>;
function request(logic: boolean): Promise<number>;
function request(logic: boolean): Promise<any>;
function request(logic: boolean) {
return new Promise((resolve, reject) => {
if (logic)
resolve("something");
resolve(1);
});
}
request(true).then((a) => {
console.log(a.length); //<-- knows that a is a string
});
request(false).then((a) => {
console.log(a.length); //<-- error: prop 'length' does not exist on number
});

Typescript returning boolean after promise resolved

I'm trying to return a boolean after a promise resolves but typescript gives an error saying
A 'get' accessor must return a value.
my code looks like.
get tokenValid(): boolean {
// Check if current time is past access token's expiration
this.storage.get('expires_at').then((expiresAt) => {
return Date.now() < expiresAt;
}).catch((err) => { return false });
}
This code is for Ionic 3 Application and the storage is Ionic Storage instance.
You can return a Promise that resolves to a boolean like this:
get tokenValid(): Promise<boolean> {
// |
// |----- Note this additional return statement.
// v
return this.storage.get('expires_at')
.then((expiresAt) => {
return Date.now() < expiresAt;
})
.catch((err) => {
return false;
});
}
The code in your question only has two return statements: one inside the Promise's then handler and one inside its catch handler. We added a third return statement inside the tokenValid() accessor, because the accessor needs to return something too.
Here is a working example in the TypeScript playground:
class StorageManager {
// stub out storage for the demo
private storage = {
get: (prop: string): Promise<any> => {
return Promise.resolve(Date.now() + 86400000);
}
};
get tokenValid(): Promise<boolean> {
return this.storage.get('expires_at')
.then((expiresAt) => {
return Date.now() < expiresAt;
})
.catch((err) => {
return false;
});
}
}
const manager = new StorageManager();
manager.tokenValid.then((result) => {
window.alert(result); // true
});
Your function should be:
get tokenValid(): Promise<Boolean> {
return new Promise((resolve, reject) => {
this.storage.get('expires_at')
.then((expiresAt) => {
resolve(Date.now() < expiresAt);
})
.catch((err) => {
reject(false);
});
});
}

RxJS sequence equivalent to promise.then()?

I used to develop a lot with promise and now I am moving to RxJS. The doc of RxJS doesn't provide a very clear example on how to move from promise chain to observer sequence.
For example, I usually write promise chain with multiple steps, like
// a function that returns a promise
getPromise()
.then(function(result) {
// do something
})
.then(function(result) {
// do something
})
.then(function(result) {
// do something
})
.catch(function(err) {
// handle error
});
How should I rewrite this promise chain in the RxJS style?
For data flow (equivalent to then):
Rx.Observable.fromPromise(...)
.flatMap(function(result) {
// do something
})
.flatMap(function(result) {
// do something
})
.subscribe(function onNext(result) {
// end of chain
}, function onError(error) {
// process the error
});
A promise can be converted into an observable with Rx.Observable.fromPromise.
Some promise operators have a direct translation. For instance RSVP.all, or jQuery.when can be replaced by Rx.Observable.forkJoin.
Keep in mind that you have a bunch of operators that allows to transform data asynchronously, and to perform tasks that you cannot or would be very hard to do with promises. Rxjs reveals all its powers with asynchronous sequences of data (sequence i.e. more than 1 asynchronous value).
For error management, the subject is a little bit more complex.
there are catch and finally operators too
retryWhen can also help to repeat a sequence in case of error
you can also deal with errors in the subscriber itself with the onError function.
For precise semantics, have a deeper look at the documentation and examples you can find on the web, or ask specific questions here.
This would definitely be a good starting point for going deeper in error management with Rxjs : https://xgrommx.github.io/rx-book/content/getting_started_with_rxjs/creating_and_querying_observable_sequences/error_handling.html
A more modern alternative:
import {from as fromPromise} from 'rxjs';
import {catchError, flatMap} from 'rxjs/operators';
fromPromise(...).pipe(
flatMap(result => {
// do something
}),
flatMap(result => {
// do something
}),
flatMap(result => {
// do something
}),
catchError(error => {
// handle error
})
)
Also note that for all this to work, you need to subscribe to this piped Observable somewhere, but I assume it's handled in some other part of the application.
Update May 2019, using RxJs 6
Agree with the provided answers above, wished to add a concrete example with some toy data & simple promises (with setTimeout) using RxJs v6 to add clarity.
Just update the passed id (currently hard-coded as 1) to something that does not exist to execute the error handling logic too. Importantly, also note the use of of with catchError message.
import { from as fromPromise, of } from "rxjs";
import { catchError, flatMap, tap } from "rxjs/operators";
const posts = [
{ title: "I love JavaScript", author: "Wes Bos", id: 1 },
{ title: "CSS!", author: "Chris Coyier", id: 2 },
{ title: "Dev tools tricks", author: "Addy Osmani", id: 3 }
];
const authors = [
{ name: "Wes Bos", twitter: "#wesbos", bio: "Canadian Developer" },
{
name: "Chris Coyier",
twitter: "#chriscoyier",
bio: "CSS Tricks and CodePen"
},
{ name: "Addy Osmani", twitter: "#addyosmani", bio: "Googler" }
];
function getPostById(id) {
return new Promise((resolve, reject) => {
setTimeout(() => {
const post = posts.find(post => post.id === id);
if (post) {
console.log("ok, post found!");
resolve(post);
} else {
reject(Error("Post not found!"));
}
}, 200);
});
}
function hydrateAuthor(post) {
return new Promise((resolve, reject) => {
setTimeout(() => {
const authorDetails = authors.find(person => person.name === post.author);
if (authorDetails) {
post.author = authorDetails;
console.log("ok, post hydrated with author info");
resolve(post);
} else {
reject(Error("Author not Found!"));
}
}, 200);
});
}
function dehydratePostTitle(post) {
return new Promise((resolve, reject) => {
setTimeout(() => {
delete post.title;
console.log("ok, applied transformation to remove title");
resolve(post);
}, 200);
});
}
// ok, here is how it looks regarding this question..
let source$ = fromPromise(getPostById(1)).pipe(
flatMap(post => {
return hydrateAuthor(post);
}),
flatMap(post => {
return dehydratePostTitle(post);
}),
catchError(error => of(`Caught error: ${error}`))
);
source$.subscribe(console.log);
Output Data:
ok, post found!
ok, post hydrated with author info
ok, applied transformation to remove title
{ author:
{ name: 'Wes Bos',
twitter: '#wesbos',
bio: 'Canadian Developer' },
id: 1 }
The key part, is equivalent to the following using plain promise control flow:
getPostById(1)
.then(post => {
return hydrateAuthor(post);
})
.then(post => {
return dehydratePostTitle(post);
})
.then(author => {
console.log(author);
})
.catch(err => {
console.error(err);
});
If I understood correctly, you mean consuming the values, in which case you use sbuscribe i.e.
const arrObservable = from([1,2,3,4,5,6,7,8]);
arrObservable.subscribe(number => console.log(num) );
Additionally, you can just turn the observable to a promise using toPromise() as shown:
arrObservable.toPromise().then()
if getPromise function is in a middle of a stream pipe you should simple wrap it into one of functions mergeMap, switchMap or concatMap (usually mergeMap):
stream$.pipe(
mergeMap(data => getPromise(data)),
filter(...),
map(...)
).subscribe(...);
if you want to start your stream with getPromise() then wrap it into from function:
import {from} from 'rxjs';
from(getPromise()).pipe(
filter(...)
map(...)
).subscribe(...);
As far as i just found out, if you return a result in a flatMap, it converts it to an Array, even if you returned a string.
But if you return an Observable, that observable can return a string;
This is how I did it.
Previously
public fetchContacts(onCompleteFn: (response: gapi.client.Response<gapi.client.people.ListConnectionsResponse>) => void) {
const request = gapi.client.people.people.connections.list({
resourceName: 'people/me',
pageSize: 100,
personFields: 'phoneNumbers,organizations,emailAddresses,names'
}).then(response => {
onCompleteFn(response as gapi.client.Response<gapi.client.people.ListConnectionsResponse>);
});
}
// caller:
this.gapi.fetchContacts((rsp: gapi.client.Response<gapi.client.people.ListConnectionsResponse>) => {
// handle rsp;
});
After(ly?)
public fetchContacts(): Observable<gapi.client.Response<gapi.client.people.ListConnectionsResponse>> {
return from(
new Promise((resolve, reject) => {
gapi.client.people.people.connections.list({
resourceName: 'people/me',
pageSize: 100,
personFields: 'phoneNumbers,organizations,emailAddresses,names'
}).then(result => {
resolve(result);
});
})
).pipe(map((result: gapi.client.Response<gapi.client.people.ListConnectionsResponse>) => {
return result; //map is not really required if you not changing anything in the response. you can just return the from() and caller would subscribe to it.
}));
}
// caller
this.gapi.fetchContacts().subscribe(((rsp: gapi.client.Response<gapi.client.people.ListConnectionsResponse>) => {
// handle rsp
}), (error) => {
// handle error
});
RxJS sequence equivalent to promise.then()?
For example
function getdata1 (argument) {
return this.http.get(url)
.map((res: Response) => res.json());
}
function getdata2 (argument) {
return this.http.get(url)
.map((res: Response) => res.json());
}
getdata1.subscribe((data1: any) => {
console.log("got data one. get data 2 now");
getdata2.subscribe((data2: any) => {
console.log("got data one and two here");
});
});

Categories