2

I've read that having an async inside a Promise is anti-pattern for async/await. The code below works, but I am curious how else to achieve the same result without having async in Promise.

If I remove it, the linter would tell how I can't use await in my mongodb query. If I remove the await in the mongodb query, then it wouldn't wait for the result.

export const getEmployees = (companyId) => {
  return new Promise(async (resolve, reject) => {
    const employees = await Employees.find(
      { companyId },
    );

    // other logic here...

    resolve({
      employees,
    });
  });

Thanks.

Woppi
  • 5,303
  • 11
  • 57
  • 81

2 Answers2

4

async functions automatically return Promises already, which resolve with whatever expression is eventually returned. Simply make getEmployees an async function:

export const getEmployees = async (companyId) => {
  const employees = await Employees.find(
    { companyId },
  );

  // other logic here...

  return { employees };
};

(but make sure to catch in the consumer of getEmployees just in case there's an error)

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • Thank you so much. I didn't know that. So in the consumer, it is something like `const employees = await getEmployees(companyId)`. What do you mean by try/catch, currently, I'm doing try/catch inside the promise... not the consumer who calls it like the answer in this: https://stackoverflow.com/a/40886720/2098493 – Woppi Jun 30 '18 at 11:42
  • 1
    Mm, the consumer is an async function too? Because the consumer is what needs to know whether an error was thrown, you could `let employees; try { employees = await getEmployees(companyId); } catch(e) { /* handle */ }` – CertainPerformance Jun 30 '18 at 11:46
  • 1
    @Woppi - One of the rules of promises (and thus, `async`/`await`, since `async`/`await` is "just" syntactic sugar for promise creation and consumption) is: You must either handle errors, or propagate the promise to something that handles errors (and so on up the chain; something, somewhere must handle the error). So if you're using `getEmployees` in another `async` function, that other function's caller must either handle (via `try`/`catch`) or propagate (by returning a promise that derives from the `getEmployees` promise). – T.J. Crowder Jun 30 '18 at 11:49
  • @T.J. Crowder, thanks for the insight. I think I found packages for that like https://github.com/scopsy/await-to-js and https://github.com/ymatuhin/flatry – Woppi Jun 30 '18 at 11:57
  • 1
    @Woppi - You don't need packages for it. Just handle errors. Particularly with `async`/`await`, it's trivial. – T.J. Crowder Jun 30 '18 at 12:00
  • But I like their one liner try/catch :) But yep, you certainly have a point. Thank you so much. – Woppi Jun 30 '18 at 12:06
  • 1
    @AnthonyWinzlet - `async`/`await` are defined by the latest JavaScript spec (ES2018), which was **just** released, and are supported in the most recent versions of Chrome, Firefox, Edge, and Node.js. More: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function – T.J. Crowder Jun 30 '18 at 13:20
  • 1
    @T.J.Crowder got it... thank you very much for the clarification – Ashh Jun 30 '18 at 13:21
  • @AnthonyWinzlet - No worries! – T.J. Crowder Jun 30 '18 at 13:59
  • @CertainPerformance - Can you please add an example of the try/catch block? Is it like this? `async function get_employees() { try { var employees = await getEmployees(x); } catch(err) { console.log("error: " + err) } }` – user1063287 Nov 13 '18 at 15:11
2

As @CertainPerformance answered, that is perfect way to retrieve data from mongoDB using async/await, I would like to add some more information about how to handle errors in this case for correctness of the system, and better error handle to return better status to the client about his request.

I'd say it , you usually want to catch all exceptions from async/await call.

try {
    const employees = await Employees.find({
        companyId
    });
    // You can add more logic here before return the data.
    return {
        employees
    };
} catch (error) {
    console.error(error);
}

Now let's check the ways we can handle our errors that might occur.

  1. Handle error inside error scope.
  2. Assign a default value to the variable in the catch block.
  3. Inspect error instance and act accordingly.

This is the most common way to handle errors in those cases and most elegant way in my opinion. Handle error inside error scope:

export const getEmployees = async (companyId) => {
    try {
        const employees = await Employees.find({
            companyId
        });
        // You can add more logic here before return the data.
        return {
            employees
        };
    } catch (error) {
        console.error(error);
    }
};

Assign a default value to the variable in the catch block:

export const getEmployees = async (companyId) => {
    let employees;

    try {
        employees = await Employees.find({
            companyId
        });
        // You can add more logic here before return the data.
        employees = employees;
    } catch (error) {
        console.error(error);
    }

    if (employees) { // We received the data successfully.
        console.log(employees)
        // Business logic goes here.
    }

    return employees;
};

Inspect error instance and act accordingly:

export const getEmployees = async (companyId) => {
    try {
        const employees = await Employees.find({
            companyId
        });
        // You can add more logic here before return the data.
        return {
            employees
        };
    } catch (error) {
        if (error instanceof ConnectionError) {
            console.error(error);
        } else {
            throw error;
        }
    }
};

Some more explanations about async await and more useful methods that you can find in those answers. How run async / await in parallel in Javascript

Moshe Binieli
  • 343
  • 3
  • 21