Невозможно получить доступ к внешней области видимости внутри цепочки обещаний, используемых в цикле for - PullRequest
0 голосов
/ 22 апреля 2020

Мне нужно сделать async/await внутри for..loop, но таким образом я не могу выполнять параллельные операции.

Так что я запихиваю эти обещания в переменную promises, позже я сделаю promise.all().

Вот мой код.

const promises = [];
  for (let i = 0; i < todayAssignedJobs.length; i += 1) {
    promises.push(Leaderboard.findOrCreate({...}).then((leaderboard, created) => {
      if (!created) {
        const rating = (todayAssignedJobs[i].rating + leaderboard.rating * leaderboard.jobs_completed) / (leaderboard.jobs_completed + 1);
        const commission = todayAssignedJobs[i].commission + leaderboard.commission;
        const jobsCompleted = leaderboard.jobs_completed + 1;
        Leaderboard.update({
          rating,
          commission,
          jobs_completed: jobsCompleted,
          updated_by: 'system',
        }, {
          where: {
            id: leaderboard.id,
          },
        });
      }
      AssignedJob.update({
        is_leaderboard_generated: true,
      }, {
        where: {
          id: todayAssignedJobs[i].id,
        },
      });
    }));
  }
await Promise.all(promises)

Почему-то, Я не могу получить идентификатор, когда я Assigned.update({..}, { where: { id: todayAssignedJobs[i].id }})

Получение ошибки:

Необработанный отказ Ошибка: ГДЕ параметр "id" имеет недопустимое значение "undefined"

Может кто-нибудь объяснить, что происходит? Также, пожалуйста, предложите, я могу сделать ниже?

promises.push(async () => { // I will use await here })

Ответы [ 2 ]

1 голос
/ 22 апреля 2020

Просто некоторые дальнейшие улучшения в существующем ответе.

Leaderboard.update() и AssignedJob.update() - это асинхронные c функции, которые следует ожидать, поэтому функцию обратного вызова необходимо преобразовать в async функция. Это гарантирует, что обещание не будет выполнено до тех пор, пока не будут выполнены все операции с базой данных, а не только findOrCreate():

const promises = todayAssignedJobs.map(async todayAssigned => {
  const [leaderboard, created] = await Leaderboard.findOrCreate({...});

  if (!created) {
    const rating = (todayAssigned.rating + leaderboard.rating * leaderboard.jobs_completed) / (leaderboard.jobs_completed + 1);
    const commission = todayAssigned.commission + leaderboard.commission;
    const jobsCompleted = leaderboard.jobs_completed + 1;

    await Leaderboard.update({
      rating,
      commission,
      jobs_completed: jobsCompleted,
      updated_by: 'system',
    }, {
      where: {
        id: leaderboard.id,
      },
    });
  }

  await AssignedJob.update({
    is_leaderboard_generated: true,
  }, {
    where: {
      id: todayAssigned.id,
    },
  });
});

await Promise.all(promises);

. Более фундаментальная проблема с этим подходом состоит в том, что Leaderboard.findOrCreate() и Leaderboard.update() не является частью одной транзакции. Это проблематично c, поскольку update() зависит от текущего значения записи в leaderboard, что создает состояние гонки в вашей базе данных из-за модификации non-atomi c запись:

const rating = (todayAssigned.rating + leaderboard.rating * leaderboard.jobs_completed) / (leaderboard.jobs_completed + 1);
const commission = todayAssigned.commission + leaderboard.commission;
const jobsCompleted = leaderboard.jobs_completed + 1;

Каждый из методов должен быть помечен как часть одной транзакции. С помощью sequelize. js вы можете достичь этого, используя управляемую транзакцию :

const promises = todayAssignedJobs.map(
  todayAssigned => sequelize.transaction(async transaction => {
    const [leaderboard, created] = await Leaderboard.findOrCreate({
      transaction,
      ...
    });

    if (!created) {
      const rating = (todayAssigned.rating + leaderboard.rating * leaderboard.jobs_completed) / (leaderboard.jobs_completed + 1);
      const commission = todayAssigned.commission + leaderboard.commission;
      const jobsCompleted = leaderboard.jobs_completed + 1;

      await Leaderboard.update({
        rating,
        commission,
        jobs_completed: jobsCompleted,
        updated_by: 'system',
      }, {
        transaction,
        where: {
          id: leaderboard.id,
        },
      });
    }

    await AssignedJob.update({
      is_leaderboard_generated: true,
    }, {
      transaction,
      where: {
        id: todayAssigned.id,
      },
    })
  })
);

await Promise.all(promises);
1 голос
/ 22 апреля 2020

Проблема была в том, что findOrCreate() возвращает массив с двумя значениями [ {...}, boolean ]

Я передавал их (leaderboard, created) здесь created всегда было undefined и leaderboard array.

Я внес изменения, и теперь он работает нормально.

const promises = todayAssignedJobs.map((todayAssigned) => Leaderboard.findOrCreate({...}).then(([leaderboard, created]) => {
    if (!created) {
      const rating = (todayAssigned.rating + leaderboard.rating * leaderboard.jobs_completed) / (leaderboard.jobs_completed + 1);
      const commission = todayAssigned.commission + leaderboard.commission;
      const jobsCompleted = leaderboard.jobs_completed + 1;
      Leaderboard.update({
        rating,
        commission,
        jobs_completed: jobsCompleted,
        updated_by: 'system',
      }, {
        where: {
          id: leaderboard.id,
        },
      });
    }
    AssignedJob.update({
      is_leaderboard_generated: true,
    }, {
      where: {
        id: todayAssigned.id,
      },
    });
  }));

await Promise.all(promises);
...