'How to use Promise.all() with Typescript
Here is what I want to do:
Promise.all([aurelia.start(), entityManagerProvider.initialize()])
.then((results:Array<any>) => {
let aurelia: any = results[0];
aurelia.setRoot();
});
aurelia.start()
returns an Aurelia type, while initialize()
returns void.
The compiler gives an error message that the type cannot be inferred from the usage.
What I am trying to achieve is to get them to run at the same time, as they are both very long processes, then run Aurelia.setRoot();
Solution 1:[1]
Its generally best to have arrays with consistent types. You can do the following manually though (passing in generic arguments):
Promise.all<Aurelia, void>(
[aurelia.start(), entityManagerProvider.initialize()
])
.then(results => {
let aurelia = results[0];
aurelia.setRoot();
});
Solution 2:[2]
Since Promise::all
is a generic function, you can declare the return types of each promise like this:
Promise.all<Aurelia, void>([
aurelia.start(),
entityManagerProvider.initialize()
])
.then(([aurelia]) => aurelia.setRoot());
Solution 3:[3]
At least from TypeScript 2.7.1
onwards, the compiler seems to resolve the types without help, with a syntax like this:
Promise.all([fooPromise, barPromise]).then(([foo, bar]) => {
// compiler correctly warns if someField not found from foo's type
console.log(foo.someField);
});
Hat tip: @JamieBirch (from comment to @AndrewKirkegaard's answer)
Solution 4:[4]
If you'd like to keep type-safety, it's possible to extend the native type-definition of the Promise
object (of type PromiseConstructor
) with additional overload signatures for when Promise.all
is called with a finite number of not-necessarily inter-assignable values:
interface PromiseConstructor
{
all<T1, T2>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>]): Promise<[T1, T2]>;
all<T1, T2, T3>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>]): Promise<[T1, T2, T3]>;
...
}
Add as many overloads as you need. This approach provides full type-safety for all elements in the value
argument of the onfulfilled
callback:
Promise.all([1, "string", true]).then(value =>
{
let a: number = value[0]; // OK
let b: number = value[1]; // Type 'string' is not assignable to type 'number'.
...
});
Solution 5:[5]
PART 1
There are functions you need to understand A) Promise.all
and B) Promise.then
:
A) the type definition of Promise.all
is a function:
all<T>(values: readonly (T | PromiseLike<T>)[]): Promise<T[]>;
B) the type definition of Promise.then
is a function that is a bit more complex:
then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): Promise<TResult1 | TResult2>;
PART 1.a
Promise.then
's type definition is a lot but it can be broken down into small parts:
then<TResult1 = T, TResult2 = never>
a function then
with 2 generics TResult1, TResult2
. The < >
means we can set and use values inside them later - they are called generics.
The then
function itself: (onfulfilled?: ..., onrejected?: ...): Promise<TResult1 | TResult2>
.
PromiseLike
is a helper type and the same as Promise
(for an intro lesson).
onfulfiled
and onrejected
are functions in the form of: (value: T) => (TResult1 OR PromiseLike<TResult1>)
OR undefined
OR null
. Notice the generic T
is used here.
PART 2 -
Promise itself has a generic interface: interface Promise<T>
. The <T>
is a/the generic.
So when you call
Promise.all<SomeCoolType>([a(), b(), c()]).then( value => doSomething(value) )
your generic is SomeCoolType
and in this example some cool type is
interface SomeCoolType = [A() => string, B() => boolean, C() => number]
Now remember that A B C
have to be Promises. And this makes it so your value
in .then( value => ...
is going to the result of SomeCoolType
which for us is calling all those functions, the result is [string, boolean, number]
.
CONCLUSION -
Concretely, the array of function/promises you pass into you Promise.all<T>
are generics that are used in .then(result => ...)
. The return/resolve value of those promises will become the value/type of result
.
Example: Promise.all<[Promise<() => string>]>([returnStringAsync()]).then(result => console.log(typeof result === "string")); # => true
Solution 6:[6]
For anyone looking for a way to use Promise.all in React TypeScript:
Promise.all<Foo, Bar>(fooPromise, barPromise)
where fooPromise
and barPromise
are the promises you want to execute in parallel, fooPromise
returns a foo
type response, and barPromise
returns a bar
type response.
If you check the response at .then
, you'll see the response is a tuple: [Foo, Bar]
.
Solution 7:[7]
When using promises that return values, my favorite using of Promise.all()
is using array destructuring as below. Using await
is more readable and destructuring is filling up the right variables.
export interface IDeveVersionHubDataService {
getBuildFilterDeveVersions(): Promise<IBuildFilterDeveVersionDto[]>;
getBuildFilterUsers(): Promise<IBuildFilterUserDto[]>;
}
const loadDeveVersions = deveVersionHubDataService.getBuildFilterDeveVersions();
const loadUsers = deveVersionHubDataService.getBuildFilterUsers();
const [deveVersions, users] = await Promise.all([loadDeveVersions, loadUsers]);
Solution 8:[8]
A full example:
type returnString = string;
type returnNumberType = number;
type jobsType = [Promise<returnString>, Promise<returnNumberType>];
const returnString = async (): Promise<returnString> => {
return 'This is a string';
};
const returnNumber = async (): Promise<returnNumberType> => {
return 123;
};
const jobs = [];
jobs.push(returnString());
if (Math.random() > 0.5) {
jobs.push(returnNumber());
}
(async () => {
const results = await Promise.all(jobs as jobsType);
// results now has the correct.
// tslint:disable-next-line: no-console
console.log(results);
})();
Solution 9:[9]
I somehow landed here when I was looking for return type of Promise.all()
since straightforward [Promise<any>, Promise<any>]
is obviously not working.
Eventually it turned out to be simplier than it seems:
const severalMongoDbOperations: Promise<[DeleteWriteOpResultObject, UpdateWriteOpResult]> =
() => Promise.all([
mongo.deleteOne({ ... }),
mongo.updateOne({ ... })
]);
Later it can be used with .then()
or:
try {
const ops: [DeleteWriteOpResultObject, UpdateWriteOpResult] = await severalMongoDbOperations();
} catch (e) {
// Process rejection
}
Solution 10:[10]
I have the same issue with you, but with this code, all work perfectly.
type TList = Promise<Aurelia> | Promise<void>;
const foo: TList[] = [aurelia.start(), entityManagerProvider.initialize()];
Promise.all<TList>(foo).then((results) => {
let aurelia = results[0];
aurelia.setRoot();
});
Solution 11:[11]
Promise.all returns "a single Promise that resolves to an array of the results of the input promises" (from MDN).
You can get the results of all the promises if you await the promise all.
const [tomorrow, today, yesterday] = await Promise.all([
fun('tomorrow'),
fun('today'),
fun('yesterday')
]);
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | |
Solution 3 | |
Solution 4 | |
Solution 5 | Taysky |
Solution 6 | noobmaster007 |
Solution 7 | |
Solution 8 | Henry Zhuang |
Solution 9 | Paul T. Rawkeen |
Solution 10 | AndriyFM |
Solution 11 |