0

I am trying to update/create users. I created a loop for the users file and want to execute it in batches of 50 users. When I push the function to an array, the values are correct, but when the functions are executed in the array, the values are suddenly repeating.

for (const user of usersjson.users) {//Loop through new users from external systems
  i++;
  try {
    const data = await getUser(url, okapikey, user[externalSystemId], 'externalSystemId');
    if (data.users.length != 0) { //If user exists
        console.log(createduser);
        functions.push(()=> updateUser(url, okapikey, createduser, data.users[0].id));
    } else { //If user doesn't exist
        console.log(createduser);
        functions.push(()=> createNewUser(url, okapikey, createduser));
    };
  } catch (err){
    console.error(err);
  }
  if (functions.length == 50 || i == usersjson.users.length) {
    const promises = functions.map(fn => fn()); //Run functions
    const responses = await Promise.allSettled(promises);
      for (const response of responses) {
        //console.log(response);
        if (response.status == 'fulfilled') {
          if (response.value.status == 204) {
            console.log(`${response.value.status}: User ${response.value.request.path.substring(7)} was updated.`);
          } else {
            if (response.value.status == 201 && response.value.headers.location) {
            console.log(`${response.value.status}: User ${response.value.headers['location']} was created.`);
            } else {
              console.log(response.value.headers.location);
            }
          }
        } else {
          console.log(response);
        }
      }
    functions=[];
  }
}

//I'm not putting the get user function, as that one works fine

async function updateUser(url, token, user, userid)
{
    console.log(user);
    return new Promise((resolve, reject) => {
        //Create headers for put request
        const options = {
            method: "put",
            headers: {
            'x-okapi-token': token,
            'x-okapi-tenant':'fs00001060',
            'Content-type':"application/json"
            }
        };
        //Make API get call
        user.id=userid; //Adding the required field ID to the JSON
        axios.put(`${url}/users/${userid}`, JSON.stringify(user), options)
            .then(response => {
              if (response.status == 204) {
                resolve(response);
              } else {
                reject(`Error Code: ${err.response.status}\nError Text: ${err.response.data.errors[0].message}\nError Status: ${err}`);
              }
            }).catch((err) => {
            if (typeof err.response.data == 'string') {
              reject(`${userid}Error Code: ${err.response.status}Error Text: ${err.response.data}Status: ${err}`);
            } else if (err.response.data.errors[0].message) {
                console.error(`Error Text: ${err.response.data.errors[0].message}`);
                reject(`Error Code: ${err.response.status}\nError Text: ${err.response.data.errors[0].message}\nError Status: ${err}`);
            } else {
              reject(`Error Code: ${err.response.status}\nError Text: ${err.response.data.errors[0].message}\nError Status: ${err}`);
            }
            });
    });
};

async function createNewUser (url, token, user) {
    console.log(user);
    return new Promise((resolve, reject) => {
        //Create headers for put request
        const options = {
            headers: {
            'X-Okapi-token': token,
            'Content-type':"application/json"
            }
        };
        //Make API get call
        axios.post(`${url}/users`, JSON.stringify(user), options)
            .then(response => {
            if (response.status == 201) {
              resolve(response);
            } else {
              reject(`Error Code: ${err.response.status}: ${user.externalSystemId},\nError Text: ${err.response.data.errors[0].message},\nError Status: ${err}`)
            }
            }).catch((err) => {
            console.error(`Error on ${user.externalSystemId}: ${err}`);
            if (err.response.data && typeof err.response.data == 'string') {
                console.error(err.response.data);
                reject(`Error Code: ${err.response.status}: ${user.externalSystemId},\nError Text: ${err.response.data.errors[0].message},\nError Status: ${err}`)
            } else if (err.response.data.errors[0].message) {
                console.error(`Error Text: ${err.response.data.errors[0].message}`);
                reject(`Error Code: ${err.response.status}: ${user.externalSystemId},\nError Text: ${err.response.data.errors[0].message},\nError Status: ${err}`)
            } else {
              reject(`Error Code: ${err.response.status}: ${user.externalSystemId},\nError Text: ${err.response.data.errors[0].message},\nError Status: ${err}`)
            }
            });
    });
};

When I console.log(createduser) I see that different users are correctly created. For example:

{
  username: 'X1',
  externalSystemId: 'email1',
  personal: {
    lastName: 'lname1',
    firstName: 'fname1',
    email: 'email1'
  },
  id: 'id1'
}
{
  username: 'X2',
  externalSystemId: 'email2',
  personal: {
    lastName: 'lname2',
    firstName: 'fname2',
    email: 'email2'
  },
  id: 'id2'
}

However, when the console.log(user) from inside the functions themselves come in to play, suddenly the users are identical (except for the id, because that is inserted separately in the code):

{
  username: 'X2',
  externalSystemId: 'email2',
  personal: {
    lastName: 'lname2',
    firstName: 'fname2',
    email: 'email2'
  },
  id: 'id1'
}
{
  username: 'X2',
  externalSystemId: 'email2',
  personal: {
    lastName: 'lname2',
    firstName: 'fname2',
    email: 'email2'
  },
  id: 'id2'
}

It's as if the functions are not saved to the array with the parameters that are passed to them, and instead just take the parameters on the last run of the loop, and inserts them for all the functions in the array.

Nimrod Yanai
  • 777
  • 2
  • 8
  • 30
  • Sounds like the [classic *closure in a loop* problem](https://stackoverflow.com/q/750486/1048572). What is `createdUser`? Where (and how) is it declared, where is it assigned? It didn't look like a loop variable, it's neither `user` nor `data`. – Bergi May 24 '21 at 22:30
  • @Bergi Thanks! Placing the "let" statements in the loops instead of outside of the loop worked liked a charm! There is a whole bunch of code that is not here because it's irrelevant to the issue. The result of the user I show in console.log(createduser) show that it was created correctly, each time the loop goes off I see a different user, so I know the issue was not there, but that closure in a loop problem solved the issue! – Nimrod Yanai May 25 '21 at 07:29

0 Answers0