I have two async methods that are independent of each other that I would like to run simultaneously to save time. However, to continue with the following step, I need to wait for both the return values from the two methods. My code looks something like this:
public async methodOne(): Promise<MyObjectOne> {
... do something interesting
return one;
}
public async methodTwo(): Promise<MyObjectTwo> {
... do something interesting
return two;
}
public useVariables(one: MyObjectOne, two: MyObjectTwo) {
... do some useful work
}
public async runTogether(): Promise<void> {
let one: MyObjectOne;
let two: MyObjectTwo;
await Promise.all([this.methodOne(), this.methodTwo()])
.then((values: [MyObjectOne, MyObjectTwo]) => {
values.map((value: MyObjectOne | MyObjectTwo) => {
if (value instanceof MyObjectOne) {
one = value;
} else {
two = value;
}
});
})
.catch((error) => {
throw new Error('There was an error');
});
this.useVariables(one, two);
}
Based on my best understanding, by the time the program reaches the last line this.useVariables(one, two);
, either both the variables are set or an error has been thrown.
But the IDE is indicating on both variables that the Variable 'one/two' is used before being assigned
.
I can check for undefined just before using the two variables to get rid of the errors. But I'm wondering if this message is indicating that I've missed something. Or is it simply that the IDE does not recognizing that the variables are set? Alternatively, is there a better way to run the two methods simultaneously?
CodePudding user response:
Assigning to an outside variable inside a callback is somewhat of a code smell, and also something that TypeScript has moderate issues with. It can't tell whether the callback will run, and so will produce that warning.
For a smaller example that produces the same error, see:
let numOuter: number;
[].forEach((num) => {
numOuter = num;
});
console.log(numOuter); // Variable 'numOuter' is used before being assigned.
The error is more justified in your case, because - what if an error is thrown? Then one
and two
will still be undefined
- doing this.useVariables(one, two);
would be unsafe, because you don't know for sure if the process succeeded.
TypeScript works best when you use can structure code to use const
whenever you can. Use instead:
public async runTogether(): Promise<void> {
const [one, two] = await Promise.all([this.methodOne(), this.methodTwo()]);
this.useVariables(one, two);
}
(make sure that methodOne
is typed to return a type of MyObjectOne
, and the same for methodTwo
)
Because runTogether
returns a Promise, the possible error that methodOne
or methodTwo
throw should almost surely be passed up the call chain to the caller, for the caller to handle - that way, the caller can see if there was a problem or not, and handle it appropriately. If you handle errors inside runTogether
itself, the caller won't be able to see that there was an error unless you re-throw. There are use cases for that, but it's a bit ugly.