gpt4 book ai didi

javascript - 如何有条件地从头开始重新启动 promise 链?

转载 作者:太空宇宙 更新时间:2023-11-04 02:03:32 26 4
gpt4 key购买 nike

我正在尝试实现一个简单的抽奖系统,其中我执行 GET/test,它返回一个随机用户,该用户 (1) 之前没有赢得过抽奖并且 (2) 在过去一小时内注册过。

在 Mongo 中,由于用户可以注册多个主题,因此可能有多个文档与用户关联。例如 {id: 1, name: 'John', subject: 'math',...}{id: 1, name: 'John', subject: 'english',...}。如果约翰被选中参加数学抽奖,那么他将没有资格参加所有后续抽奖,因此他无法多次获胜。本质上,抽奖者的 id 必须是唯一的。

我的问题是:我如何执行逻辑来检查约翰之前是否赢过?如果约翰已经获胜,我想从顶部重新启动 promise 链并再次执行 Math.random 直到选出唯一的获胜者。如果没有获胜者符合条件,那么我想返回 res.status(500)

   app.get('/test', function(req, res, next) {
var currentTimestamp = new Date()
var oneHourAgo = new Date(currentTimestamp - oneHour)
var query = { "timeStamp": { $lte: currentTimestamp, $gte: oneHourAgo }, "isWinner": false }
var winna = {}
var winnerSelected = false

var collection = db.collection('entries');

while (!winnerSelected) { // how can I do this
collection.find(query).toArray().then( result => {
var winner = result[Math.floor(Math.random() * result.length)];
var query2 = {"id" : winner.id};
winna['id'] = winner.id
winna['name'] = winner.name
winna['subject'] = winner.subject
winna['timeStamp'] = winner.timeStamp
winna['isWinner'] = winner.isWinner
winna['raffleTimestamp'] = winner.raffleTimestamp

return collection.find(query2).toArray();
}).then( result => {
for (var i in result) { // a winner can enter the raffle for multiple subjects, but if he already won once, then I want to redraw by doing a rand function again
if (i.isWinner) { // until a winner who is eligible is found, or if none are eligible, res.status(500)
console.log("i already won")
break
// how do I make it go back to the beginning of the while loop and pick a new random winner?
}
}

console.log("unique winner")
winnerSelected = true // break out of while loop
var query3 = { id: winna.id, subject: winna.subject }
var raffleTimestamp = new Date()
var update = { $set: { isWinner: true, raffleTimestamp: raffleTimestamp } }
winna['isWinner'] = true
winna['raffleTimestamp'] = raffleTimestamp
res.send(winna) // send the winner with the updated fields to clientside
return collection.updateOne(query3, update); // update the isWinner and raffleTimestamp fields
}).then( result => {
res.status(200);
// res.send(result);
}).catch( err => {
console.log(err);
res.status(500);
});
}
})

最佳答案

简而言之,在这种情况下您实际上不需要这样做。但还有一个更长的解释。

如果您的 MongoDB 版本支持它,那么您可以简单地使用 $sample在初始查询条件之后聚合管道以获得“随机”选择。

当然,在任何情况下,如果有人因为已经“获胜”而没有资格,那么只需将他们标记为这样,或者直接在另一组表格结果中标记。但这里“排除”的一般情况是简单地修改查询以将“获胜者”从可能的结果中排除。

但是,我实际上将至少在“现代”意义上演示“打破循环”,即使您实际上并不需要它来完成这里实际需要做的事情,这实际上是修改查询以排除。

const MongoClient = require('mongodb').MongoClient,
whilst = require('async').whilst,
BPromise = require('bluebird');

const users = [
'Bill',
'Ted',
'Fred',
'Fleur',
'Ginny',
'Harry'
];

function log (data) {
console.log(JSON.stringify(data,undefined,2))
}

const oneHour = ( 1000 * 60 * 60 );

(async function() {

let db;

try {
db = await MongoClient.connect('mongodb://localhost/raffle');

const collection = db.collection('users');

// Clean data
await collection.remove({});

// Insert some data
let inserted = await collection.insertMany(
users.map( name =>
Object.assign({ name },
( name !== 'Harry' )
? { updated: new Date() }
: { updated: new Date( new Date() - (oneHour * 2) ) }
)
)
);
log(inserted);

// Loop with aggregate $sample
console.log("Aggregate $sample");

while (true) {
let winner = (await collection.aggregate([
{ "$match": {
"updated": {
"$gte": new Date( new Date() - oneHour ),
"$lt": new Date()
},
"isWinner": { "$ne": true }
}},
{ "$sample": { "size": 1 } }
]).toArray())[0];

if ( winner !== undefined ) {
log(winner); // Picked winner
await collection.update(
{ "_id": winner._id },
{ "$set": { "isWinner": true } }
);
continue;
}
break;
}

// Reset data state
await collection.updateMany({},{ "$unset": { "isWinner": "" } });

// Loop with random length
console.log("Math random selection");
while (true) {
let winners = await collection.find({
"updated": {
"$gte": new Date( new Date() - oneHour ),
"$lt": new Date()
},
"isWinner": { "$ne": true }
}).toArray();

if ( winners.length > 0 ) {
let winner = winners[Math.floor(Math.random() * winners.length)];
log(winner);
await collection.update(
{ "_id": winner._id },
{ "$set": { "isWinner": true } }
);
continue;
}
break;
}

// Reset data state
await collection.updateMany({},{ "$unset": { "isWinner": "" } });

// Loop async.whilst
console.log("async.whilst");

// Wrap in a promise to await
await new Promise((resolve,reject) => {
var looping = true;
whilst(
() => looping,
(callback) => {
collection.find({
"updated": {
"$gte": new Date( new Date() - oneHour ),
"$lt": new Date()
},
"isWinner": { "$ne": true }
})
.toArray()
.then(winners => {
if ( winners.length > 0 ) {
let winner = winners[Math.floor(Math.random() * winners.length)];
log(winner);
return collection.update(
{ "_id": winner._id },
{ "$set": { "isWinner": true } }
);
} else {
looping = false;
return
}
})
.then(() => callback())
.catch(err => callback(err))
},
(err) => {
if (err) reject(err);
resolve();
}
);
});

// Reset data state
await collection.updateMany({},{ "$unset": { "isWinner": "" } });

// Or synatax for Bluebird coroutine where no async/await
console.log("Bluebird coroutine");

await BPromise.coroutine(function* () {
while(true) {
let winners = yield collection.find({
"updated": {
"$gte": new Date( new Date() - oneHour ),
"$lt": new Date()
},
"isWinner": { "$ne": true }
}).toArray();

if ( winners.length > 0 ) {
let winner = winners[Math.floor(Math.random() * winners.length)];
log(winner);
yield collection.update(
{ "_id": winner._id },
{ "$set": { "isWinner": true } }
);
continue;
}
break;
}
})();

} catch(e) {
console.error(e)
} finally {
db.close()
}
})()

当然,无论采用哪种方法,每次结果都是随机的,并且以前的“获胜者”被排除在实际查询本身的选择之外。这里的“循环中断”只是用来不断输出结果,直到不再有可能的获胜者为止。

<小时/>

关于“循环中断”方法的注释

现代 Node.js 环境中的一般建议是内置的 async/await/yield 功能,现在在 v8.x.x 版本中默认启用。这些版本将于今年 10 月(截至撰写本文时)提供长期支持(LTS),并且按照我个人的“三个月规则”,那么任何新作品都应该基于当时流行的内容。

此处的替代案例通过 async.await 提供作为单独的库依赖项。或者使用“Bluebird”Promise.coroutine作为单独的库依赖项,后一种情况是您可以交替使用 Promise.try ,但是如果您要包含一个库来获取该函数,那么您不妨使用另一个实现更现代语法方法的函数。

因此,“虽然”(双关语不是故意的)演示“打破 promise /回调”循环,但真正应该从这里去掉的主要内容是不同的查询过程,它实际上执行了在选择随机获胜者之前尝试在“循环”中实现的“排除”。

实际情况是数据决定了这一点。但整个示例至少展示了可以应用“选择”和“循环中断”的方法。

关于javascript - 如何有条件地从头开始重新启动 promise 链?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45123212/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com