By freedomflyer


2017-06-20 22:15:16 8 Comments

I like the flatness of the new Async/Await feature available in Typescript, etc. However, I'm not sure I like the fact that I have to declare the variable I'm awaiting on the outside of a try...catch block in order to use it later. Like so:

let createdUser
try {
    createdUser = await this.User.create(userInfo)
} catch (error) {
    console.error(error)
}

console.log(createdUser)
// business
// logic
// goes
// here

Please correct me if I'm wrong, but it seems to be best practice not to place multiple lines of business logic in the try body, so I'm left only with the alternative of declaring createdUser outside the block, assigning it in the block, and then using it after.

What is best practice in this instance?

4 comments

@Arik 2019-11-12 16:59:13

I usually use the Promise's catch() function to return an object with an error property on failure.

For example, in your case i'd do:

const createdUser = await this.User.create(userInfo)
          .catch(error => { error }); // <--- the added catch

if (Object(createdUser).error) {
    console.error(error)
}

If you don't like to keep adding the catch() calls, you can add a helper function to the Function's prototype:

Function.prototype.withCatcher = function withCatcher() {
    const result = this.apply(this, arguments);
    if (!Object(result).catch) {
        throw `${this.name}() must return a Promise when using withCatcher()`;
    }
    return result.catch(error => ({ error }));
};

And now you'll be able to do:

const createdUser = await this.User.create.withCatcher(userInfo);
if (Object(result).error) {
    console.error(result);
}

@Ivan 2019-02-15 15:34:17

@Bergi Answer is good, but I think it's not the best way because you have to go back to the old then() method, so i think a better way is to catch the error in the async function

async function someAsyncFunction(){
    const createdUser = await this.User.create(userInfo);

    console.log(createdUser)
}

someAsyncFunction().catch(console.log);
  • But what if we have many await in the same function and need to catch every error?

You may declare the to() function

function to(promise) {
    return promise.then(data => {
        return [null, data];
    })
    .catch(err => [err]);
}

And then

async function someAsyncFunction(){
    let err, createdUser, anotherUser;

    [err, createdUser] = await to(this.User.create(userInfo));

    if (err) console.log(`Error is ${err}`);
    else console.log(`createdUser is ${createdUser}`);


    [err, anotherUser] = await to(this.User.create(anotherUserInfo));

    if (err) console.log(`Error is ${err}`);
    else console.log(`anotherUser is ${anotherUser}`);
}

someAsyncFunction();

When reading this its: "Wait to this.User.create".

Finally you can create the module "to.js" or simply use the await-to-js module.

You can get more information about to function in this post

@Bergi 2019-02-25 19:54:01

then isn't worse than await because it older. It's just different, and suited for other things. This "await to(…) style" on the other hand is reminiscent of the nodeback style with all its disadvantages.

@Bergi 2019-02-25 20:02:48

Btw, for better performance and simplicity you should use promise.then(data => [null, data], err => [err, null]);

@Ivan 2019-02-27 04:07:50

Exactly "It's just different, and suited for other things" await is used for create a code with a "synchronous" like syntax, the use of then and it's callback is more asynchronous syntax. Btw thanks for the code simplicity recommendation :)

@nevf 2019-01-16 22:30:40

Another simpler approach is to append .catch to the promise function. ex:

const createdUser = await this.User.create(userInfo).catch( error => {
// handle error
})

@Bergi 2017-06-20 22:32:28

It seems to be best practice not to place multiple lines of business logic in the try body

Actually I'd say it is. You usually want to catch all exceptions from working with the value:

try {
    const createdUser = await this.User.create(userInfo);

    console.log(createdUser)
    // business logic goes here
} catch (error) {
    console.error(error) // from creation or business logic
}

If you want to catch and handle errors only from the promise, you have three choices:

  • Declare the variable outside, and branch depending on whether there was an exception or not. That can take various forms, like

    • assign a default value to the variable in the catch block
    • return early or re-throw an exception from the catch block
    • set a flag whether the catch block caught an exception, and test for it in an if condition
    • test for the value of the variable to have been assigned

    let createdUser; // or use `var` inside the block
    try {
        createdUser = await this.User.create(userInfo);
    } catch (error) {
        console.error(error) // from creation
    }
    if (createdUser) { // user was successfully created
        console.log(createdUser)
        // business logic goes here
    }
    
  • Test the caught exception for its type, and handle or rethrow it based on that.

    try {
        const createdUser = await this.User.create(userInfo);
        // user was successfully created
        console.log(createdUser)
        // business logic goes here
    } catch (error) {
        if (error instanceof CreationError) {
            console.error(error) // from creation
        } else {
            throw error;
        }
    }
    

    Unfortunately, standard JavaScript (still) doesn't have syntax support for conditional exceptions.

  • Use then with two callbacks instead of try/catch. This really is the least ugly way and my personal recommendation also for its simplicity and correctness, not relying on tagged errors or looks of the result value to distinguish between fulfillment and rejection of the promise:

    await this.User.create(userInfo).then(createdUser => {
        // user was successfully created
        console.log(createdUser)
        // business logic goes here
    }, error => {
        console.error(error) // from creation
    });
    

    Of course it comes with the drawback of introducing callback functions, meaning you cannot as easily break/continue loops or do early returns from the outer function.

@dcorking 2018-01-24 14:57:39

Your last example uses .then() to resolve the promise and provide a callback, so perhaps await has no effect there.

@Bergi 2018-01-24 20:32:25

@dcorking It is awaiting the promise returned by the .then(…) call.

@dcorking 2018-01-25 07:56:12

do the lambdas returned by the .then() call need the async keyword?

@Bergi 2018-01-25 08:20:04

@dcorking Only if they use await inside.

@Saroj 2019-02-25 17:44:35

I have seen people attaching catch handler directly to await . Is it a good Idea to do that or wrap it inside try/catch?

@Bergi 2019-02-25 18:08:34

@Saroj const result = await something().catch(err => fallback); is simpler than let result; try { result = await something(); } catch(err) { result = fallback; } so yes in that case I consider it a good idea.

Related Questions

Sponsored Content

44 Answered Questions

[SOLVED] What is the preferred syntax for defining enums in JavaScript?

6 Answered Questions

[SOLVED] Syntax for async arrow function

15 Answered Questions

[SOLVED] Using async/await with a forEach loop

21 Answered Questions

[SOLVED] How and when to use ‘async’ and ‘await’

7 Answered Questions

[SOLVED] try/catch blocks with async/await

1 Answered Questions

2 Answered Questions

9 Answered Questions

[SOLVED] Await in catch block

5 Answered Questions

[SOLVED] Try-catch speeding up my code?

1 Answered Questions

Sponsored Content