使用asynchronouswhile循环创build和插入文档

我试图生成令牌,并将其保存到数据库,因为我这样做。

这是生成令牌的代码。

const generateToken = function (maxUse) { // 12 digit token numbers. 9e+11 possibilities const min = 100000000000; const max = 999999999999; const token = Math.floor(Math.random() * (max -min) + min); // ensure token doesn't exit exist in db before saving Token.count({ token }, function (err, count) { if (count > 0) { generateToken() ; } else { let newToken = new Token({ token, maxUse }); newToken.save(function (err, savedToken) { if (err) { console.log(err); return; } else { generateSerial(savedToken._id); console.log("saved token is =>", savedToken.token); return savedToken.token; } }) } }) } 

如何编写一个函数来调用这个函数的任意次数,并将这些标记附加到一个文件,因为它们被保存到数据库中。 我意识到while循环不会因为进程的asynchronous性质而不起作用。

我所见过的所有答案都假设我有批量数据,比如使用bulkwrite(mongoose)。

另一种方法是受欢迎的

谢谢。

我看到这个的方式,你可能最好保留生成的令牌的“本地列表”,并通过.insertMany()插入“批量”。 里程可能会根据实际情况而有所不同,所以我们将讨论这种方法,以及用asynchronous方法以一种合理的方式处理recursion函数。

asynchronous循环

您已经创build了一个问题,您需要testing存在的值以确定它们对于插入是“唯一”的。 这当然需要asynchronous调用才能查看数据库,因此在“循环”发送之前不知道该项是否存在,因此排除了“批量”操作,如“upserts”。 所以recursion在这种情况下工作。

所以你应该做的第一件事就是使“函数”本身asynchronous,或者返回一个callback或者一个承诺。

在本质上:

 function generateToken(maxUse) { const min = 100000000000; const max = 999999999999; const token = Math.floor(Math.random() * (max -min) + min); return Token.count({ token }).then( count => { if ( count > 0 ) { generateToken(maxUse); } else { return Token.create({ token, maxUse }); } }) } 

或者以更现代的方式与asynchronous/等待

 async function generateToken(maxUse) { const min = 100000000000; const max = 999999999999; const token = Math.floor(Math.random() * (max -min) + min); let count = await Token.count({ token }); if ( count > 0 ) { generateToken(maxUse); } else { return Token.create({ token, maxUse }); } } 

那么这只是一个循环调用的问题,或者用现代的术语来说:

 let count = 0; while (count < 500) { // Random usage 1-5 const maxUse = Math.floor(Math.random() * 5) + 1; let token = await generateToken(maxUse); log(token.token); count++; } 

或者使用async.whilst如果在不支持async/await的节点版本下运行:

  asyncWhilst( () => count < 500, (callback) => { const maxUse = Math.floor(Math.random() * 5 ) + 1; generateToken(maxUse).then(token => { log(token.token); count++; callback(); }).catch(err => callback(err)); }, (err) => { if (err) throw err; // Loop complete, issue callback or promise } ); 

所以这一切都相对简单。


保持唯一的本地和“批量插入”

处理这个“替代”的方法是将生成的令牌的数组保存在“客户端”上。 然后,你需要做的每一个随机生成是看看令牌是否“已经看到”,只有在获得“唯一”值时创build一个插入操作。

这应该比recursion调用数据库快得多,因为它在本地都是“caching”的。

实质上,使您的发电机function非常基本:

 function generateToken(maxUse) { const min = 100000000000; const max = 999999999999; const token = Math.floor(Math.random() * (max -min) + min); return ({ token, maxUse }); } 

然后在循环过程中,为seenTokensopsops创build两个数组,其中后者表示稍后将以批量批量插入而不是单个写入的项目:

 let count = 0, seenTokens = [], ops = []; while ( count < 500 ) { const maxUse = Math.floor(Math.random() * 5) + 1; let token = generateToken(maxUse); if ( seenTokens.indexOf(token.token) === -1 ) { seenTokens.push(token.token); ops.push(token); count++ if ( count % 500 === 0 ) { await Token.insertMany(ops); ops = []; } } else { continue } } if ( count % 500 !== 0 ) { await Token.insertMany(ops); ops = []; } 

当然,我们在那里应用async/await方法,但是这仅仅是.insertMany()方法是asynchronous的,如果你实际上没有插入“成千上万”的方法,那么它应该很容易处理,甚至不需要“等待”这样的电话,然后只发出“一次”

但是这里的演示说明了当代码“成千上万”时代码应该看起来像什么,没有其他改变。 您也可以使用其他库函数来根据需要“等待”这样的调用。

再次,我们可以使用async.seriesasync.whilst进行这样的控制:

  let count = 0, seenTokens = [], ops = []; asyncSeries( [ (callback) => asyncWhilst( () => count < 500, (callback) => { const maxUse = Math.floor(Math.random() * 5) + 1; let token = generateToken(maxUse); if ( seenTokens.indexOf(token.token) === -1 ) { seenTokens.push(token.token); ops.push(token); count++; if ( count % 500 === 0 ) { Token.insertMany(ops,(err,response) => { console.log(count); ops = []; callback(err); }); } else { callback(); } } else { console.log("trying again: seen token %s", token.token); callback(); } }, callback ), (callback) => { if ( count % 500 !== 0 ) { Token.insertMany(ops,callback) } else { callback() } } ], (err) => { if (err) throw err; ops = []; // Operations complete, so callback to continue } ); 

完全一样,“stream量控制”实际上只是为了满足“大批量”的要求,您可以简单地使用常规循环来构buildops条目,并且只对.insertMany()因为这里的500限制实际上是这样。

所以最简单的forms基本上是:

 let count = 0, seenTokens = [], ops = []; // Regular loop while ( count < 500 ) { const maxUse = Math.floor(Math.random() * 5) + 1; let token = generateToken(maxUse); if ( seenTokens.indexOf(token.token) === -1 ) { seenTokens.push(token.token); ops.push(token); count++; } } // Insert all at once Token.insertMany(ops,(err,result) => { if (err) throw err; // now it's complete }) 

当然,这种替代方法“取决于”你从来没有真正维护数据库中的“标记”的“持久性”,并且不会再次调用这个函数,直到这些现有的条目被清除。 我们可以在所有“被采取的令牌”中“啜食”,并且被相同的“本地caching”排除。 但是随着时间的推移,这将会显着增长,所以在你的整体select中要考虑这一点。


作为一个完整的列表脚手架的最新nodejs发布,但一般的用法是应用在里面:

 const asyncWhilst = require('async').whilst, mongoose = require('mongoose'), Schema = mongoose.Schema; mongoose.Promise = global.Promise; mongoose.set('debug', true); const uri = 'mongodb://localhost/test', options = { useMongoClient: true }; const tokenSchema = new Schema({ token: { type: Number, unique: true }, maxUse: Number }); const Token = mongoose.model('Token', tokenSchema); // Logger helper function log(data) { console.log(JSON.stringify(data,undefined,2)) } // Function implementation function generateToken(maxUse) { const min = 100000000000; const max = 999999999999; const token = Math.floor(Math.random() * (max -min) + min); return Token.count({ token }).then( count => { if ( count > 0 ) { generateToken(maxUse); } else { return Token.create({ token, maxUse }); } }) } // Main program (async function() { try { const conn = await mongoose.connect(uri,options); console.log("using async/await"); // clean data await Promise.all( Object.keys(conn.models).map(m => conn.models[m].remove({})) ); let count = 0; while (count < 500) { // Random usage 1-5 const maxUse = Math.floor(Math.random() * 5) + 1; let token = await generateToken(maxUse); log(token.token); count++; } let totalCount = await Token.count(); console.log("Count is: %s", totalCount); // Or using async.whilst console.log("Using async.whilst"); // clean data await Promise.all( Object.keys(conn.models).map(m => conn.models[m].remove({})) ); count = 0; await new Promise((resolve,reject) => { asyncWhilst( () => count < 500, (callback) => { const maxUse = Math.floor(Math.random() * 5 ) + 1; generateToken(maxUse).then(token => { log(token.token); count++; callback(); }).catch(err => callback(err)); }, (err) => { if (err) reject(err); resolve(); } ); }); totalCount = await Token.count(); console.log("Count is: %s", totalCount); } catch (e) { console.error(e); } finally { mongoose.disconnect(); } })(); 

或者作为一个“替代”过程:

 const asyncSeries = require('async').series, asyncWhilst = require('async').whilst, mongoose = require('mongoose'), Schema = mongoose.Schema; mongoose.Promise = global.Promise; mongoose.set('debug', true); const uri = 'mongodb://localhost/test', options = { useMongoClient: true }; const tokenSchema = new Schema({ token: { type: Number, unique: true }, maxUse: Number }); const Token = mongoose.model('Token', tokenSchema); // Logger helper function log(data) { console.log(JSON.stringify(data,undefined,2)) } // Function implementation function generateToken(maxUse) { const min = 100000000000; const max = 999999999999; const token = Math.floor(Math.random() * (max -min) + min); return ({ token, maxUse }); } // Main program (async function() { try { const conn = await mongoose.connect(uri,options); console.log("Using async/await"); // clean data await Promise.all( Object.keys(conn.models).map(m => conn.models[m].remove({})) ); let count = 0, seenTokens = [], ops = []; while ( count < 500 ) { const maxUse = Math.floor(Math.random() * 5) + 1; let token = generateToken(maxUse); if ( seenTokens.indexOf(token.token) === -1 ) { seenTokens.push(token.token); ops.push(token); count++ if ( count % 500 === 0 ) { await Token.insertMany(ops); ops = []; } } else { continue } } if ( count % 500 !== 0 ) { await Token.insertMany(ops); ops = []; } totalCount = await Token.count(); console.log("Count is: %s", totalCount); // using async.whilst and indeed async.series for control console.log("using asyc.whilst"); await Promise.all( Object.keys(conn.models).map(m => conn.models[m].remove({})) ); await new Promise((resolve,reject) => { count = 0, seenTokens = [], ops = []; asyncSeries( [ (callback) => asyncWhilst( () => count < 500, (callback) => { const maxUse = Math.floor(Math.random() * 5) + 1; let token = generateToken(maxUse); if ( seenTokens.indexOf(token.token) === -1 ) { seenTokens.push(token.token); ops.push(token); count++; if ( count % 500 === 0 ) { Token.insertMany(ops,(err,response) => { console.log(count); ops = []; callback(err); }); } else { callback(); } } else { console.log("trying again: seen token %s", token.token); callback(); } }, callback ), (callback) => { if ( count % 500 !== 0 ) { Token.insertMany(ops,callback) } else { callback() } } ], (err) => { if (err) reject(err); ops = []; resolve(); } ); }); totalCount = await Token.count(); console.log("Count is: %s", totalCount); } catch (e) { console.error(e); } finally { mongoose.disconnect(); } })();