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.
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.
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
});
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);
});
});
}
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");
});
});