This proposal allows the Typescript language service to automatically refactor code that uses Promise methods such as .then() or .catch() to instead use the async and await keywords.
Async/await offers many advantages over promise methods including cleaner syntax, error handling, debugging, and readability. The benefits of the synchronous style code provided by async/await are largely recognized by the Javascript community. However, there is still a substantial amount of asynchronous code written using Promises. This proposal will provide a quick and simple transition to using the async/await keywords.

Input:
function ex1(): Promise<boolean> {
return fetch('https://microsoft.com').then( result => result.ok; );
}
Output:
async function ex1(): Promise<boolean> {
let result = await fetch('https://microsoft.com');
return result.ok;
}
````
---
**onRejected handlers:**
**Input:**
```ts
function ex2(): Promise<void> {
return fetch('https://microsoft.com').then( result => console.log(result), rej => console.log("error", rej) );
}
Output:
async function ex2(): Promise<void> {
let result;
try {
result = await fetch('https://microsoft.com');
}
catch (rej) {
return console.log("error:", rej);
}
return result.ok;
}
Note that this conversion does not preserve semantics. In order to create clean, readable code, the semantics of some code is slightly altered.
Catch handlers:
Input:
ts
function ex3():Promise<void> {
return fetch('https://microsoft.com').then(result => console.log(result)).catch(err => console.log(err));
}
Output:
async function ex3():Promise<void> {
let result;
try {
result = await fetch('https://microsoft.com');
await console.log(result);
}
catch (err) {
await console.log(err);
}
}
````
---
## More Examples - multiple handlers
**Multiple .then() calls:**
In situations where variable names intersect, a new variable name will be chosen for intermediate variables. For example:
**Input:**
````ts
function ex4():Promise<boolean> {
return fetch('https://microsoft.com').then(res).then(res2);
}
function res(result){
return result.ok;
}
function res2(result){
console.log(result);
}
````
**Output:**
````ts
async function ex4():Promise<boolean> {
let result = await fetch('https://microsoft.com');
let temp = await res(result);
return res2(temp);
}
function res(result){
return result.ok;
}
function res2(result){
console.log(result);
}
````
**Multiple .catch() calls**
**Input:**
````ts
function ex5(): Promise<void> {
return fetch('https://microsoft.com').then(res => console.log(res)).catch(err => console.log("err")).catch(err2 => console.log("err2", err2));
}
````
**Output:**
````ts
async function ex5(): Promise<void> {
try {
let res;
try {
res = await fetch("https://microsoft.com");
return console.log(res);
}
catch (err) {
return console.log("err");
}
}
catch (err2) {
return console.log("err2");
}
}
````
## More Examples - Promise.all() and Promise.race()
In order to preserve semantics, code that uses `Promise.all()` cannot be entirely refactored.
**Input**
```ts
function ex6():Promise<void> {
return Promise.all([fetch('https://microsoft.com'), fetch('https://microsoft.com'), fetch('https://youtube.com')]).then(
function(vals){
vals.forEach(console.log);
});
}
Output:
ts
async function ex6():Promise<void> {
let vals = await Promise.all([fetch('https://microsoft.com'), fetch('https://microsoft.com'), fetch('https://youtube.com')]);
return vals.forEach(console.log);
}
Similarly, code that uses 'Promise.race()' is partially refactored:
Input
ts
function ex7():Promise<void> {
return Promise.race([fetch('https://microsoft.com'), fetch('https://microsoft.com'), fetch('https://youtube.com')]).then(val => console.log(val));
}
Output:
ts
async function ex7():Promise<void> {
let val = await Promise.race([fetch('https://microsoft.com'), fetch('https://microsoft.com'), fetch('https://youtube.com')]);
return console.log(val);
}
In order to preserve semantics, a refactoring will only be offered for functions that directly return a promise with callbacks.
For example, a refactoring will not be offered for the following:
function ex8() {
let blob = fetch("https://typescriptlang.org").then(resp => console.log(resp));
return blob;
}
However, a refactoring will be offered for the following function as it can be converted to use async and await while preserving semantics:
function ex9() {
return fetch("https://typescriptlang.org").then(resp => console.log(resp));
}
Support for .finally() will be coming later
I think that the promises should be const instead of let by default.
Also, the output in the Promise.all example should not return, as the forEach is not returned in the original function
I wanted to make this as VSCode extension for quite some time. Landing this in typescript of course makes much more sense, because this way any IDE integrating the language server can offer this refactor very easily. Awesome stuff-is there a code somewhere which does the refactor itself @elizabethdinella ?
How will this deal with things that are like Promises but not quite?
PromiseLike?Promise?Promise in your tsconfig lib, but do have a class named Promise?Promise in your tsconfig lib, and have a class that also happens to be named Promise?Edit: oh, and is it out of scope to suggest this fix?
const sample = async () => {
if (condition) {
// Just return;
return Promise.resolve();
}
};
So for one of your catch handler example, you have
async function foo():Promise<void> {
let result;
try {
result = await fetch('https://microsoft.com');
await console.log(result);
}
catch (err) {
await console.log(err);
}
}
Ideally result would be declared inside of the try block. It would be better as
async function foo():Promise<void> {
- let result;
try {
- result = await fetch('https://microsoft.com');
+ let result = await fetch('https://microsoft.com');
await console.log(result);
}
catch (err) {
await console.log(err);
}
}
Can you give specific labels to examples? It'd make it easier to reference 😃
For similar reasons, this is also problematic:
async function foo(): Promise<void> {
let result;
try {
result = await fetch('https://microsoft.com');
}
catch (rej) {
return console.log("error:", rej);
}
return result.ok;
}
Here, you will actually introduce a failure if a rejection occurred, since result will be undefined. Ideally all of your test cases ensure that no errors have been introduced under --strict mode.
resultwill beundefined
@DanielRosenwasser How would result being undefined be a problem here? Wouldn't the function already return on the line after the catch? As far as I can see it wouldn't ever reach the last line where result.ok is referenced unless the fetch succeeded...
Most helpful comment
I think that the promises should be
constinstead ofletby default.Also, the output in the
Promise.allexample should not return, as theforEachis not returned in the original function