1

Working case:

async await is working fine when we call a asynchronous function and that function returning a promise resolve()

Not working case:

async await is not working for mongo DB queries

tried then(), async/await

I have 2 JS files.

In one.js file i am importing function which is in functionone.js

WORKING CASE:

When one.js looks like

var functiononestatus = transactions.functionone(req.session.email).then((came) => {
  console.log(came); // getting `need to be done at first` message
  console.log("exec next")
});

When functionone.js looks like

module.exports.functionone = functionone;

async function functionone(email) {
  return await new Promise((resolve, reject) => {
    resolve('need to be done at first')
  });
});

NOT WORKING CASE (when mongo db query need to be executed):

When one.js looks like

var functiononestatus = transactions.functionone(req.session.email).then((came) => {
  console.log(came); // getting undefined
  console.log("exec next")
});

When functionone.js looks like

module.exports.functionone = functionone;

async function functionone(email) {

  //mongo starts
  var collection = await connection.get().collection('allinonestores');
  await collection.find({
    "email": email
  }).toArray(async function(err, wallcheck) {
    return await new Promise((resolve, reject) => {
      resolve(wallcheck[0])
    });
  });
Erdogan Oksuz
  • 621
  • 11
  • 17
  • 1
    In the second, `functionone` isn't returning anything, thus `undefined` – CertainPerformance Jan 04 '19 at 06:00
  • actually we are getting values in `wallcheck[0]` (in the second function) but code one.js is getting undefined eventhough we have values in `wallcheck[0]` –  Jan 04 '19 at 06:01
  • 1
    Yes, because your `functionone` is not returning anything. – CertainPerformance Jan 04 '19 at 06:02
  • I understood that, but y is the question? –  Jan 04 '19 at 06:03
  • 1
    It's not returning anything because you have no `return` statement in the function. Try using `return` to return the Promise. – CertainPerformance Jan 04 '19 at 06:04
  • could you please give me a brief answer. –  Jan 04 '19 at 06:05
  • I wrote return statement, please check. –  Jan 04 '19 at 06:06
  • @JanakiRajeshDuvvuri the `return` of `return await new Promise` does not belong to the function `functionone` but to the function you pass as callback to `toArray` so you do not return anything from `functionone` – t.niese Jan 04 '19 at 06:12
  • @t.niese could u please give the answer. –  Jan 04 '19 at 06:14
  • @JanakiRajeshDuvvuri I would have written a complete answer if I would know all necessary details. Are you sure that the `toArray` function is supposed to return what you return in its callback? Without knowing the mongodb module I still strongly doubt that this should be the case, because that would contradicts all naming conventions. Or how do you come to the assumption that `toArray` should do that, where is that mention in the documentation? – t.niese Jan 04 '19 at 07:12

2 Answers2

5

Quick clarification:

  1. .collection('name') returns a Collection instance, not a Promise, so no need to await for it.
  2. toArray() operates in two modes: either with a callback when a function is provided, either returns a Promise when no callback function is provided.

You're essentially expecting a Promise result out of toArray() while supplying a callback function, resulting in undefined, because callback takes priority and no promise is returned, due to the dual operation mode of toArray().

Also, toArray(callback) does not take an async function as callback.

Here's how your code should look like, for retrieving a collection:

const client = await MongoClient.connect('your mongodb url');
const db = client.db('your database name'); // No await here, because it returns a Db instance.
const collection = db.collection('allinonestores'); // No await here, because it returns a Collection instance.

and then, code for fetching results:

const db = <get db somehow>;

// You could even ditch the "async" keyword here,
// because you do not do/need any awaits inside the function.
// toArray() without a callback function argument already returns a promise.
async function functionOne(email) {

  // Returns a Collection instance, not a Promise, so no need for await.
  const collection = db.collection('allinonestore');

  // Without a callback, toArray() returns a Promise.
  // Because our functionOne is an "async" function, you do not need "await" for the return value.
  return collection.find({"email": email}).toArray();
}

and code alternative, using callback:

const db = <get db somehow>;

// You could even ditch the "async" keyword here,
// because you do not do/need any awaits inside the function.
// You're forced to return a new Promise, in order to wrap the callback
// handling inside it
async function functionOne(email) {

  // Returns a Collection instance, not a Promise, so no need for await.
  const collection = db.collection('allinonestore');

  // We need to create the promise outside the callback here.
  return new Promise((resolve, reject) => {
    db.find({"email": email}).toArray(function toArrayCallback(err, documents) {
       if (!err) {
         // No error occurred, so we can solve the promise now.
         resolve(documents);
       } else {
         // Failed to execute the find query OR fetching results as array someway failed.
         // Reject the promise.
         reject(err);
       }
    });
  });
}
Mihai Potra
  • 838
  • 4
  • 6
  • 1
    `[...]Also, toArray(callback) does not take an async function as callback.[...]` is a bit misleading, for `toArray` it does not really matter if the function is `async` or not (as it does not use the return value), so it would be perfectly fine to pass a `async` function to `toArray` if you want/need to use `await` in that function. – t.niese Jan 04 '19 at 07:47
  • 1
    Indeed, callback execution does not prohibit async functions. However, it's a bad practice to write async functions where the caller does not handle promise return values, because aside performance costs it also creates the confusion that you can return promises/do awaits in it, which is false: the async callback will never be awaited on in this case, and all promises inside it will be settled way after the callback already executed. – Mihai Potra Jan 04 '19 at 07:53
  • That's not true, otherwise you should consider ever asynchronous action you do in a callback function that does not use the result of that asynchronous action as result as bad practice. If you e.g. use express js for any other library involving routing, then you have something like this `app.get('/path', (req, res, next) => res.send('ok'))`. So now you might need to do something async in it, Then you can for sure write `app.get('/path', async (req, res, next) => try{ res.send(await req.db.find()) } catch( err ) {next(err)})` even so `app.get` does not use the Promise returned by the callback. – t.niese Jan 04 '19 at 08:28
  • My actual problem is I need to return success message if documents are != empty else failure message, but i am getting undefined when the return message is inside the mongo query as mentioned in the problem. –  Jan 04 '19 at 08:55
  • 1
    @t.niese Mihai Potra is definitely right in the case of callbacks that are executed only once, one should rather create a `new Promise` for that case than nesting `async function`s. And even your express example I wouldn't consider good style, as it makes that `try`/`catch` necessary. It's [easier to write](https://stackoverflow.com/a/41350872/1048572) `app.get('/path', promiseWrapper(async (req, res) => { res.send(await req.db.find()); }));` – Bergi Jan 04 '19 at 09:12
  • You don't need to use `async` keyword for function if you don't use `await` inside of it – ponury-kostek Jan 04 '19 at 09:59
  • @t.niese Everything I said is true. Furthermore, you're bringing in express examples that have nothing to do with the question here. Secondly, in your express examples, next/send/etc are calls express waits for before continuing; the entire chain is asynchronous. In the case of `Cursor.toArray()` the callback execution is syncronous and while the language permits async functions and awaiting inside, those calls are on another execution loop that runs _after_ the callback for toArray() executed. – Mihai Potra Jan 04 '19 at 18:06
  • @Janaki if you need to reject on empty collection, you could either handle the promise inside the function, or create another function: `async function functionTwo(email) { const docs = await functionOne(email); if (docs.length > 0) { return docs; } else { throw new TypeError('No results'); } }` – Mihai Potra Jan 04 '19 at 18:11
1

Note: First of all i really need to thank @mihai Potra for the best answer.

Here we go

Case 1:

If it is a function which need to find documents and return from MongoDb as mihai mentioned, below answer is uber cool

const db = <get db somehow>;
async function functionOne(email) {
  const collection = db.collection('allinonestore');
  return collection.find({"email": email}).toArray();
}

case 2:

If there are nested functions which need to return values every time below ans will be the best as of my knowledge

-no need async/await keywords for every function or no need then()

function one(<parameter>) {
return new Promise(function(resolve, reject) {
const collection = connection.get().collection('<collection_name>');
const docs = collection.find({"Key": Value_fromFunction}).toArray( function (err, result) {
resolve(result[0]);
});

That's it, use resolve callback when ever it needed.