Skip to content

Proposal: Code Fix -- Convert Promise Handlers to Async and Await #25082

Closed
@elizabethdinella

Description

@elizabethdinella

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.

promisetoasync2

Simple Examples

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:

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:

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:

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:

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:

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:

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

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:

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

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:

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);
}

More Examples - Returning variables of Promise type

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

Metadata

Metadata

Labels

CommittedThe team has roadmapped this issueDomain: Refactoringse.g. extract to constant or function, rename symbolSuggestionAn idea for TypeScript

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions