RandyBeans
RandyBeans

Reputation: 109

Node Js: Call async await within map not working

I'm creating a tinder clone. Im currently creating the match/post request in my backend. Within the post request itself it calls a separate function named match. This is called after the current user as liked or disliked another account. Basically all I need this function to do is loop through that account's likes to see if it matches the current users id. As I am using MongoDB I need to be able to make use of async await to interact with my database. However the function never runs past the if statement. I'm thinking it has something to do with calling an async function within .map(). However I am a bit stumped on how to progress further.

require("dotenv").config();

const express = require("express");
const router = express.Router({ mergeParams: true });
const jwt = require("jsonwebtoken");
const bcryptjs = require("bcryptjs");
const cookieParser = require('cookie-parser'); 
const { check, validationResult } = require("express-validator");

const multer = require('multer');

const User = require("../models/userSchema");

const ObjectID = require('mongodb').ObjectID;

const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, 'uploads/')
  },
  filename: function (req, file, cb) {
    cb(null, Date.now() + '.jpg')
  }
})

const upload = multer({ storage: storage }) 

function asyncHandler(callback) {
  return async (req, res, next) => {
    try {
      await callback(req, res, next);
    } catch (error) {
      next(error);
      console.log(error);
    }
  };
}

function match (match, user) {
  console.log("Starting to match")

  const currentUser = user;

  console.log(currentUser._id)

  match.likes.map((person) => {

      return async( req, res, next) => {

        console.log(person._id, 'Person id')
        console.log(currentUser._id, 'Current user id')

        if (person.likes._id === currentUser._id) {
        
        console.log("Worked")

        await User.findOneAndUpdate({ _id: new ObjectID(currentUser._id) }, { $push: { matches: match } });
  
        await User.findOneAndUpdate({ _id: match._id },{ $push: { matches: currentUser } });
  
        res.json({ message: "You got a match!" }).end();
      }  
    }
  });
};


/*
Adds matches to the user 
*/

router.patch( "/user/match/:id", authenticateUser, asyncHandler(async (req, res, next) => {

  const errors = validationResult(req);

  const user = await User.findOne({ _id: req.params.id });

  if (!errors.isEmpty()) {
    const errorMessages = errors.array().map((error) => error.msg);
    return res.status(400).json({ errors: errorMessages });
  }

  const updateObject = req.body.likes;
  console.log(updateObject)

  const likedUser = await User.findOne({ _id: updateObject });


  if (updateObject) {

    await User.findOneAndUpdate( { _id: req.params.id }, { $push: { likes: updateObject } });
    console.log("Getting ready to match")
    match(likedUser, user)

  } else {

    await User.findOneAndUpdate({ _id: req.params.id }, { $push: { dislikes: updateObject } },

      function (err, doc) {
        if (err) {
          return res.json({ success: false, message: err.message });
        }

        res.json({ updateObject });

        return res.status(204).end();

      }
    );
  }

})
);
   {
      likes: [Array],
      dislikes: [],
      _id: '60bcfe5191694b0981439707',
      firstName: 'Jon',
      lastName: 'Doe',
      emailAddress: '[email protected]',
      password: '$2a$10$cOt9PcLUd7HdwzYjhBAov.OWVGQUbBVVpR2SiVSXghFWaJfX88bSm',
      gender: 'Male',
      sexualPreference: 'Straight',
      age: 26,
      description: 'Hello my name is Jon',
      path: 'uploads/1622998609804.jpg',
      matches: [],
      __v: 0
    }

The likes array is an array of objects composed like this user object.

Upvotes: 0

Views: 657

Answers (2)

Pranjal Kulshrestha
Pranjal Kulshrestha

Reputation: 36

When we use async-await inside array functions like forEach(), map(). forEach() expects a synchronous function and even when we use async/await inside, it just returns multiple promises but not in sequential order.

Solution:

  1. Either use for of loop which does your work sequentially.

  2. If you want to use Array.map() then put it inside Promise.all() so that it does your task in parallel.

Example:

await Promise.all(files.map(async (file) => {
  // your code here....
}));

Upvotes: 2

Paul Martin
Paul Martin

Reputation: 469

Hard to replicate without seeing the data, but I wouldn't recommend using .map in this situation, as you're not returning anything and they don't work well with asynchronous code. I'd use a recursive function like this...

I've used a placeholder fetch request instead of a DB update, but the principle is the same...

const recursive = async (matches, user, run = 0) => {
    if (run > matches.length) return
    console.log(run)
    if (matches[run] === user+1) {
        
      await fetch('https://jsonplaceholder.typicode.com/todos/1')
        .then(response => response.json())
        .then(json => console.log(json))
      
      return recursive(matches, user, run +1)
    } 
    else return recursive(matches, user, run +1)
  } 

Input...

recursive([1, 2, 3, 4], 2)

Output...

0
1
2
{userId: 1, id: 1, title: "delectus aut autem", completed: false}
3
4

The function won't call itself until the last iteration has finished and it will await for the api call to return something before finishing.

Upvotes: 1

Related Questions