this is the code to initialize a gRPC server I am using:
export const initServer = async (finalPort: number): Promise<string> => {
let initStatus = 'initial';
gRPCserver.addService(webcomponentHandler.service, webcomponentHandler.handler);
// define the host/port for server
await gRPCserver.bindAsync(
`localhost:${finalPort}`,
grpc.ServerCredentials.createInsecure(),
(err: Error | null, port1: number) => {
if (err != null) {
console.error(err);
log.info('Biding Error');
initStatus = 'Binding Error';
}
// start the gRPC server
gRPCserver.start();
log.info(`startServer::gRPC server started, on port: ${port1}`);
initStatus = 'OK';
},
);
return initStatus;
};
I am trying to return that initStatus to this function:
type StartServerType = () => Promise<string | undefined>;
export const startServer: StartServerType = async (): Promise<string | undefined> => {
try {
let initStatusResponse;
// Get port
const finalPort = port();
await server.initServer(finalPort).then((res) => {
initStatusResponse = res;
log.info('INIT STATUS RESPONSE: ', initStatusResponse);
});
} catch (e: unknown) {
if (typeof e === 'string') {
return e.toUpperCase(); // works, `e` narrowed to string
}
if (e instanceof Error) {
return e.message; // works, `e` narrowed to Error
}
}
return 'started';
};
But I always received 'initial' as initStatusResponse. I know there is an async issue there, but I can not see where.
Thanks in advance.
You will need to check the documentation for gRPCserver.bindAsync but it would seem that this method does not return a promise object. The likelihood of this is increased because you are passing a callback function as an argument. Promise objects remove the need for callback functions (Often referred to as "callback hell"). Here is an article that explains the differences between the different ways to do asynchronous actions in Javascript/Typescript.
Using async/await is a good approach. In order to use it if gRPCserver.bindAsync doesn't return a promise we can "wrap" it in a promise. This can be done easily with the node.js util promisify (Documentation) or manually with new Promise. This article explains both ways well.
Related
How do I set the type of the rejection of my promise? Let's say I do:
const start = (): Promise<string> => {
return new Promise((resolve, reject) => {
if (someCondition) {
resolve('correct!');
} else {
reject(-1);
}
});
}
Let's say I want to reject with a number. But I cannot set the type; I can pass whatever I want to the reject here.
Moreover, when using this promise, I want to have compiling error if I use the rejection response type incorrectly.
As explained in this issue, Promise doesn't have different types for fulfilled and rejected promises. reject accepts any argument that doesn't affect type of a promise.
Currently Promise cannot be typed any better. This results from the fact that a promise can be rejected by throwing inside then or catch (this is a preferable way to reject existing promise), and this cannot be handled by typing system; also, TypeScript also doesn't have exception-specific types except never.
Cause there is no way to set error type in some cases like Promise, or exception throws, we can work with errors in rust-like style:
// Result<T, E> is the type used for returning and propagating errors.
// It is an sum type with the variants,
// Ok<T>, representing success and containing a value, and
// Err<E>, representing error and containing an error value.
export type Ok<T> = { _tag: "Ok"; ok: T };
export type Err<E> = { _tag: "Err"; err: E };
export type Result<T, E> = Ok<T> | Err<E>;
export const Result = Object.freeze({
Ok: <T, E>(ok: T): Result<T, E> => ({ _tag: "Ok", ok }),
Err: <T, E>(err: E): Result<T, E> => ({ _tag: "Err", err }),
});
const start = (): Promise<Result<string, number>> => {
return new Promise((resolve) => {
resolve(someCondition ? Result.Ok("correct!") : Result.Err(-1));
});
};
start().then((r) => {
switch (r._tag) {
case "Ok": {
console.log(`Ok { ${r.ok} }`);
break;
}
case "Err": {
console.log(`Err { ${r.err} }`);
break;
}
}
});
The exception is typed any because we cannot guarantee the correct
type of the exception at design time, and neither TypeScript nor
JavaScript provide the ability to guard exception types at run time.
Your best option is to use type guards to provide both a design-time and run-time check in your code.
source
What #EstusFlask mentioned in his answer is correct.
But I want go one step near to an artificial solution to
simulate what we want with TypeScript capabilities.
Sometimes I use this pattern in my codes😉:
interface IMyEx{
errorId:number;
}
class MyEx implements IMyEx{
errorId:number;
constructor(errorId:number) {
this.errorId = errorId;
}
}
// -------------------------------------------------------
var prom = new Promise(function(resolve, reject) {
try {
if(..........)
resolve('Huuuraaa');
else
reject(new MyEx(100));
}
catch (error) {
reject(new MyEx(101));
}
});
// -------------------------------------------------------
prom()
.then(success => {
try {
}
catch (error) {
throw new MyEx(102);
}
})
.catch(reason=>{
const myEx = reason as IMyEx;
if (myEx && myEx.errorId) {
console.log('known error', myEx)
}else{
console.log('unknown error', reason)
}
})
You can use a proxy to explicitly force resolve and reject argument types. The following example does not try to mimic the constructor of a promise - because I didn't find that useful in practice. I actually wanted to be able to call .resolve(...) and .reject(...) as functions outside of the constructor. On the receiving side the naked promise is used - e.g., await p.promise.then(...).catch(...).
export type Promolve<ResT=void,RejT=Error> = {
promise: Promise<ResT>;
resolve: (value:ResT|PromiseLike<ResT>) => void;
reject:(value:RejT) =>void
};
export function makePromolve<ResT=void,RejT=Error>(): Promolve<ResT,RejT> {
let resolve: (value:ResT| PromiseLike<ResT>)=>void = (value:ResT| PromiseLike<ResT>)=>{}
let reject: (value:RejT)=>void = (value:RejT)=>{}
const promise = new Promise<ResT>((res,rej) => {
resolve = res;
reject = rej;
});
return { promise, resolve, reject };
}
The let statements look as if they are pointless - and they are pointless at runtime. But it stops compiler errors that were not easy to resolve otherwise.
(async()=>{
const p = makePromolve<number>();
//p.resolve("0") // compiler error
p.resolve(0);
// p.reject(1) // compiler error
p.reject(new Error('oops'));
// no attempt made to type the receiving end
// just use the named promise
const r = await p.promise.catch(e=>e);
})()
As shown, calls to .resolve and .reject are properly typed checked.
No attempt is made in the above to force type checking on the receiving side.
I did poke around with that idea, adding on .then and .catch members, but then what should they return? If they return a Promise then it goes back to being a normal promise, so it is pointless. And it seems there is no choice but to do that. So the naked promise is used for await, .then and .catch.
Here's my attempt at typing it:
export class ErrPromise<TSuccess, TError> extends Promise<TSuccess> {
constructor(executor: (resolve: (value: TSuccess | PromiseLike<TSuccess>) => void, reject: (reason: TError) => void) => void) {
super(executor);
// Object.setPrototypeOf(this, new.target.prototype); // restore prototype chain
}
}
export interface ErrPromise<TSuccess, TError = unknown> {
then<TResult1 = TSuccess, TResult2 = never>(onfulfilled?: ((value: TSuccess) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ((reason: TError) => TResult2 | PromiseLike<TResult2>) | undefined | null): Promise<TResult1 | TResult2>;
catch<TResult = never>(onrejected?: ((reason: TError) => TResult | PromiseLike<TResult>) | undefined | null): Promise<TSuccess | TResult>;
}
Use it like normal:
return new ErrPromise<T,ExecError>((resolve, reject) => { ... })
Your IDE should pick up the type of reject:
I'm building an app that interacts with smart contracts using ethers.js.
I would like to call a smart contract method (it returns a promise) and then after the execution doing some stuffs on the UI.
I did the following:
async function getAllowance(address: string) {
try {
if (contrastState != null) {
return await signedTokenContract?.allowance(address, contrastState.address)
} else throw new Error('Contract or signer is null or undefined check connect method results')
} catch (e) {
console.log('connect error', e)
}
}
Then I use the method on the UI using the useLayoutEffect:
useLayoutEffect(() => {
async function initialize() {
setLoading(true)
const allowance = await getAllowance(address).then(allowance => {
setAllowedNumber(allowance)
console.log('Then allowance ' + allowance)
setLoading(false)
})
}
initialize()
}, [])
The problem I experience is that the code in the then is called before the end of the promise, and then I get the following log:
'Then allowance: undefined'
I used promises many times. Can you tell me where I'm wrong?
Thanks
When handling promises, either use async/await or then syntax
const allowance = await getAllowance(address);
setAllowedNumber(allowance);
// OR
getAllowance(address).then(allowance => {
setAllowedNumber(allowance);
});
I am writing a hook to make a post request that returns two properties, sessionId and sessionData. I am using this hook in a component. My hook looks like this.
export const createOrder = async () => {
try {
const response = await postWithToken(API_ROUTES.PAYMENT_SESSION, token || '',
testObject)
console.log("FROM HOOK", response)
return response
} catch (err: any) {
console.log(err)
}
}
And my component look like this
const myComponent = () => {
useEffect(() => {
createOrder().then(data => {
console.log("Session Data",data.sessionData)
console.log("FROM PAGE", data)
})
}, [])
return (
<div />
)
}
When I try to access data.sessionData on the component I get the error that sessionDta does not exist on type void. But If I check the logs on the console I get the same object on the component and on the hook. Also If I check on my component the typeof data I get an object.
Why I am getting this error?
You don't return anything from your catch block, so the return type of your function is Promise<WhateverTheTypeOfResponseIs | void> (N.B. async functions implicitly return a Promise, and if your postWithToken function doesn't return anything then it's just Promise<void>), depending on which code path happens.
In the future you can avoid unpleasant and slightly problematic to debug issues like this by giving your functions an explicit return type and in this case the compiler will let you know that your expectation was violated:
const postWithToken = async (route: string, token: string, obj: any): Promise<boolean> => {
try {
const resp = await fetch(
route,
{
method: 'POST',
body: JSON.stringify(Object.assign(obj, { token }))
},
)
return Boolean(resp.status < 400 && resp.status >= 200)
} catch (err) {
console.error(err)
return false
}
}
const API_ROUTES = {
PAYMENT_SESSION: 'whatever'
}
const testObject = {
token: ''
}
const token = ''
const createOrder = async (): Promise<boolean> => { // <-- squiggles
try {
const response = await postWithToken(API_ROUTES.PAYMENT_SESSION, token || '',
testObject)
console.log("FROM HOOK", response)
return response
} catch (err: any) {
console.log(err)
}
}
Playground
The types in the example I created may be different (you'll need to sub with the actual types from your code) but you should get the idea. You can fix this by any of the following:
Explicitly return something of the correct type from the catch block.
Change your return type to Promise<CorrectType | undefined>.
Move the error handling to the caller as suggested by goto1 in the comments.
Also note that as goto1 points out in the comments on the question, your hook isn't actually a hook (which is fine, but be careful of terminology).
This sounds like a TypeScript issue, so I suggest to provide the proper return type for your createOrder function.
// Use the official type for SessionData, if you have it available,
// otherwise create a new type that properly matches its shape
type CreateOrderResult = {
sessionData: SessionData;
}
// No need for `async/await` here, just return the `postWithToken`
// and handle errors inside your component
export const createOrder = (): Promise<CreateOrderResult> => {
// ...
return postWithToken(
API_ROUTES.PAYMENT_SESSION,
token || '',
testObject
)
}
// MyComponent.tsx
const MyComponent = () => {
useEffect(() => {
createOrder()
.then(data => console.log(data.sessionData))
.catch(error => /* handle errors appropriately */)
}, [])
}
open tsconfig.json file and
change
"strict": true,
into
"strict": false,
this usually works for me
check out https://v2.vuejs.org/v2/guide/typescript.html for more information
Just wanted to preemptively say that I am familiar with async/await and promises in JavaScript so no need to link me to some MDN pages for that.
I have a function to fetch user details and display it on the UI.
async function someHttpCall() {
throw 'someHttpCall error'
}
async function fetchUserDetails() {
throw 'fetchUserDetails error'
}
function displayUserDetails(userDetails) {
console.log('userDetails:', userDetails)
}
async function fetchUser() {
try {
const user = await someHttpCall()
try {
const details = await fetchUserDetails(user)
returndisplayUserDetails(details)
} catch (fetchUserDetailsError) {
console.log('fetching user error', fetchUserDetailsError)
}
} catch (someHttpCallError) {
console.log('networking error:', someHttpCallError)
}
}
It first makes HTTP call via someHttpCall and if it succeeds then it proceeds to fetchUserDetails and it that succeeds as well then we display the details on Ui via returndisplayUserDetails.
If someHttpCall failed, we will stop and not make fetchUserDetails call. In other words, we want to separate the error handling for someHttpCall and it’s data handling from fetchUserDetails
The function I wrote is with nested try catch blocks which doesn't scale well if the nesting becomes deep and I was trying to rewrite it for better readability using plain then and catch
This was my first atttempt
function fetchUser2() {
someHttpCall()
.then(
(user) => fetchUserDetails(user),
(someHttpCallError) => {
console.log('networking error:', someHttpCallError)
}
)
.then(
(details) => {
displayUserDetails(details)
}, //
(fetchUserDetailsError) => {
console.log('fetching user error', fetchUserDetailsError)
}
)
}
The problem with this is that the second then will run i.e. displayUserDetails even with someHttpCall failing. To avoid this I had to make the previous .catch blocks throw
so this is the updated version
function fetchUser2() {
someHttpCall()
.then(
(user) => fetchUserDetails(user),
(someHttpCallError) => {
console.log('networking error:', someHttpCallError)
throw someHttpCallError
}
)
.then(
(details) => {
displayUserDetails(details)
}, //
(fetchUserDetailsError) => {
console.log('fetching user error', fetchUserDetailsError)
}
)
}
However now the second catch will get called as a result of the throw. So when the someHttpCall failed, after we handled the someHttpCallError error, we would enter this block (fetchUserDetailsError) => { console.log('fetching user error', fetchUserDetailsError) } which is not good since fetchUserDetails never gets called so we shouldn't need to handle fetchUserDetailsError (I know someHttpCallError became fetchUserDetailsError in this case)
I can add some conditional checks in there to distinguish the two errors but it seems less ideal. So I am wondering how I can improve this by using .then and .catch to achieve the same goal here.
I am wondering how I can improve this by using .then and .catch to achieve the same goal here
You don't get to avoid the nesting if you want to replicate the same behaviour:
function fetchUser2() {
return someHttpCall().then(
(user) => {
return fetchUserDetails(user).then(
(details) => {
return displayUserDetails(details)
},
(fetchUserDetailsError) => {
console.log('fetching user error', fetchUserDetailsError)
}
)
},
(someHttpCallError) => {
console.log('networking error:', someHttpCallError)
throw someHttpCallError
}
)
}
(The exact equivalent to try/catch would use .then(…).catch(…) instead of .then(…, …), but you might not actually want that.)
The function I wrote is [nested] which doesn't scale well if the nesting becomes deep and I was trying to rewrite it for better readability […]
For that, I would recommend to combine await with .catch():
async function fetchUser() {
try {
const user = await someHttpCall().catch(someHttpCallError => {
throw new Error('networking error', {cause: someHttpCallError});
});
const details = await fetchUserDetails(user).catch(fetchUserDetailsError => {
throw new Error('fetching user error', {cause: fetchUserDetailsError});
});
return displayUserDetails(details);
} catch (someError) {
console.log(someError.message, someError.cause);
}
}
(The cause option for Error is still quite new, you might need a polyfill for that)
I can add some conditional checks in there to distinguish the two errors but it seems less ideal.
Actually, that sounds like an ideal situation. That means that you don't have to nest any try / catch blocks which could make you code a lot more readable. This is one of the things that async / await is meant to solve.
A solution could be is to create custom errors by extending the Error interface to be able to determine how and where the error occurs.
class CustomError extends Error {
constructor(name, ...args) {
super(...args)
this.name = name
}
}
Throw your errors within the functions that correspond with the error.
async function someHttpCall() {
throw new CustomError('HttpCallError', 'someHttpCall error');
}
async function fetchUserDetails(user) {
throw new CustomError('UserDetailsError', 'fetchUserDetails error')
}
Now you can control your error flow by checking the name property on the error to differentiate your errors.
async function fetchUser() {
try {
const user = await someHttpCall()
const details = await fetchUserDetails(user)
return displayUserDetails(details)
} catch (error) {
switch(error.name) {
case 'HttpCallError':
console.log('Networking error:', error)
break
case 'UserDetailsError':
console.log('Fetching user error', error)
break
}
}
}
I've been inspired by Rust's Result type (which forces you to handle every potential error along the way).
So what I do is handle exceptions in every individual function, and never allow one to throw, instead returning either an Error (if something went wrong) or the desired return value (if no exception occurred). Here's an example of how I do it (comments included):
TS Playground
If you aren't familiar with TypeScript, you can see the JavaScript-only version of the following code (with no type information) at the TypeScript Playground link above (on the right side of the page).
// This is the code in my exception-handling utility module:
// exception-utils.ts
export type Result <T = void, E extends Error = Error> = T | E;
export function getError (value: unknown): Error {
return value instanceof Error ? value : new Error(String(value));
}
export function isError <T>(value: T): value is T & Error {
return value instanceof Error;
}
export function assertNotError <T>(value: T): asserts value is Exclude<T, Error> {
if (value instanceof Error) throw value;
}
// This is how to use it:
// main.ts
import {assertNotError, getError, isError, type Result} from './exception-utils.ts';
/**
* Returns either Error or string ID,
* but won't throw because it catches exceptions internally
*/
declare function getStringFromAPI1 (): Promise<Result<string>>;
/**
* Requires ID from API1. Returns either Error or final number value,
* but won't throw because it catches exceptions internally
*/
declare function getNumberFromAPI2 (id: string): Promise<Result<number>>;
/**
* Create version of second function with no parameter required:
* Returns either Error or final number value,
* but won't throw because it catches exceptions internally
*
* The previous two functions work just like this, using the utilities
*/
async function fetchValueFromAPI2 (): Promise<Result<number>> {
try {
const id = await getStringFromAPI1(); // Error or string
assertNotError(id); // throws if `id` is an Error
return getNumberFromAPI2(id); // Error or number
}
catch (ex) {
return getError(ex);
}
}
async function doSomethingWithValueFromAPI2 (): Promise<void> {
const value = await fetchValueFromAPI2(); // value is number or Error
if (isError(value)) {
// handle error
}
else console.log(value); // value is number at this point
}
Maybe a trivial one, but I am new with Typescript and fetch API.
In an exported class I have a public method remoteFetchSomething like:
export class className {
remoteFetchSomething = (url : string) : Promise<Response> => {
return fetch(url)
.then(
(r) => r.json()
)
.catch((e) => {
console.log("API errore fetching " + objectType);
});
}
}
export const classInstance = new className();
The method queries a remote JSON API service, and in the code, I am using it like:
import { classInstance } from ...
classInstance.remoteFetchSomething('https://example.url')
.then((json) => {
console.log(json);
}
)
The console.log is actually showing the results, but the remoteFetchSomething returns a Promise and I am unable to parse and access the JSON object values.
I would like to wait for the response before executing the remaining code, but how do I unwrap content from promise? Should I again put another .then? What am I missing?
Thank you.
By now I resolved the problem defining the return type of the remoteFetch as any:
remoteFetchSomething = (url : string) : any => {
return fetch(url)
.then(
(r) => r.json()
)
.catch((e) => {
console.log("API errore fetching " + objectType);
});
}
And now I can access JSON values like data below:
classInstance.remoteFetchSomething('https://example.url').then(
(json) => {
console.dump(json.data);
}
)
[sincerely still not clear why I cant' use the Promise<Response> type]
You can't synchronously block while waiting for a request in javascript, it would lock up the user's interface!
In regular javascript, and most versions of TypeScript, you should be / must be returning a promise.
function doRequestNormal(): Promise<any> {
return fetch(...).then(...);
}
function someOtherMethodNormal() {
// do some code here
doRequestNormal.then(() => {
// continue your execution here after the request
});
}
In newer versions of typescript, there's async/await keyword support - so instead it might look like this:
async function doRequestAsync() {
var result = await fetch(...);
// do something with request;
return result;
}
async function someOtherMethodAsync() {
// do some code here
var json = await doRequestAsync();
// continue some code here
}
Keep in mind, doRequestAsync still returns a Promise under the hood - but when you call it, you can use await to pretend that you're blocking on it instead of needing to use the .then( callback. If you call an async method from a non-async method, you'll still need to use the callbacks as normal.
this is how I do it:
type Payload = {
id: number
}
type ReturnType = number
export const functionThatHasNumberType = async (
payload: Payload
): Promise<ReturnType> => {
let url = `/api/${payload.id}`
return await axios.get(url)
}