Unable to get the value from Promise.allSettled in NodeJs 12 with Typescript 3.8.3 version

Viewed 13693

I am learning NodeJs 12 with Promise.allSettled() function and its usage. I have written the following code. I am able to print the status in the console but unable to print the value as it is giving compilation issue.

        const p1 = Promise.resolve(50);
        const p2 = new Promise((resolve, reject) =>
            setTimeout(reject, 100, 'geek'));
        const prm = [p1, p2];

        Promise.allSettled(prm).
        then((results) => results.forEach((result) =>
            console.log(result.status,result.value)));

I am getting the following compilation issue. enter image description here

I provide below the tsconfig.json.

{
  "compilerOptions": {
    "target": "es2017",
    "lib": ["es6","esnext", "dom"],
    "allowJs": true,
    "module": "commonjs",
    "moduleResolution": "node",
    "declaration": true,
    "outDir": "./lib",
    "strict": true,
    "esModuleInterop": true,
    "typeRoots": [ "./types", "./node_modules/@types"]
  },
  "include": ["src"],
  "exclude": ["**/__tests__/*"]
}
4 Answers

You might want something like this:

  Promise.allSettled(prm).
    then((results) => results.forEach(result => {
       if (result.status === 'fulfilled') {
         console.log(result.status,result.value);
       } else {
         console.log(result.status,result.reason);
       }
    });

value only exists if the status is fulfilled, but it doesn't cover cases where one of the promises had an error.

If one of the promises will be rejected, your object will have no value. To warn you about this possibility, TypeScript uses this type:

enter image description here

So, to get value from the result you should use guards

const isFilled = <T extends {}>(v: PromiseSettledResult<T>): v is PromiseFulfilledResult<T> => v.status === 'fulfilled';

Promise.allSettled(prm).then((results) => results.forEach((result) =>
    console.log(result.status, isFilled(result) ? result.value : result.status)));

or to cast it yourself

Promise.allSettled(prm).then((results) => results.forEach((result) =>
    console.log(result.status, (result as PromiseFulfilledResult<any>).value)));

Using typeguards to correctly type PromiseSettledResult like in but without creating a specific isFullfilled function and using filter + map

const promises = [
  Promise.reject("Error"),
  Promise.resolve(42),
  Promise.reject("Another error")
]
const results = await Promise.allSettled(promises)
const successes = results
  .filter((x): x is PromiseFulfilledResult<number> => x.status === "fulfilled")
  .map(x => x.value)
const failures: any[] = results
  .filter((x): x is PromiseRejectedResult => x.status === "rejected")
  .map(x => x.reason)

I believe the typing system is flexible enough to be extended to correctly type

const successes = results
  .filter(x => x.status === "fulfilled")
  .map(x => x.value)
const failures = results
  .filter(x => x.status === "rejected")
  .map(x => x.reason)

but I wasn't able to figure that out myself.

I see two problems here.

first

The second promise you create is of type Promise<unknown> because typescript cannot infer its underlying type.

When you create an array with the two promises, unknown "swallows" everything and you are left with an array of type Promise<unknown>[].

If you type p2 like this:

const p2 = new Promise<number>((resolve, reject) =>
  setTimeout(reject, 100, 'geek'));

you won't have this problem.

second

You probably have to do something like this for typescript to know if the promise was a success or not:

Promise.all(prm).then((results) =>
  results.forEach((result) => {
    if (result.status === "fulfilled") {
      console.log("fulfilled", result.value);
    } else {
      console.log("rejected", result.reason);
    }
  })
);

Related