无法访问 for 循环中使用的链式 Promise 内的外部范围变量
Posted
技术标签:
【中文标题】无法访问 for 循环中使用的链式 Promise 内的外部范围变量【英文标题】:Can not access outer scope variable inside chained promise used in for loop 【发布时间】:2020-08-05 06:04:16 【问题描述】:我需要在for..loop
中执行async/await
,但那样我就无法进行并行操作。
所以我将这些承诺推入一个变量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
出现错误:
未处理的拒绝错误:WHERE 参数“id”具有无效的“未定义”值
有人可以解释发生了什么吗?另外,请建议我可以在下面做吗?
promises.push(async () => // I will use await here )
【问题讨论】:
我会记录todayAssignedJobs[i]
的值。我的猜测是该对象没有id
属性,因为使用let
的for 循环为i
提供了适当的词法范围,因此异步性不是问题。对于更简洁的方法,请注意您基本上是在重新实现 Array.prototype.map()
,所以只需使用它而不是 for
循环来初始化 promises
。
@PatrickRoberts 感谢您的建议。我用map
替换了for
,现在它更干净了。 :)
一个then
回调只接受一个参数,(leaderboard, created)
没有意义
【参考方案1】:
问题是 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);
【讨论】:
【参考方案2】:只是对现有答案的一些进一步改进。
Leaderboard.update()
和AssignedJob.update()
是需要等待的异步函数,所以需要将回调函数转换为async
函数。这确保了在所有数据库操作完成之前,promise 不会被解析,而不仅仅是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()
不是单个事务的一部分。这是有问题的,因为update()
取决于leaderboard
中条目的当前值,由于non-atomic 对条目的修改,这会在您的数据库中创建一个竞争条件:
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,您可以使用 managed transaction 来实现:
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);
【讨论】:
我尝试了相同的方法,但promises
在调用 await Promise.all(promises)
后返回了一个数组 [ [AsyncFunction], [AsyncFunction] ]
。你能解释一下Promise.all(promises)
是如何在这里解决它们的吗?
知道了,但我不是在这里谈论这个问题。请看这里。我尝试了同样的方法。 ***.com/questions/61337600/…
完全不一样,你的问题已经被别人彻底解答了。
但是这一行 第一个问题是 Promise.all 接受一个承诺数组,而不是一个函数数组——你当前的代码不会工作。在这里做同样的事情吗?将函数数组传递给Promise.all()
@SujeetAgrahari 按照您的逻辑,let x = [1, 2, 3, 4, 5].map(v => v * 2)
将使x
成为一个函数数组。 promises
是 promises 数组,而不是函数。以上是关于无法访问 for 循环中使用的链式 Promise 内的外部范围变量的主要内容,如果未能解决你的问题,请参考以下文章
PromiseKit `Result .. is used warning` 在带有 for 循环的链式序列中