正确处理Node Promise中的asynchronousMongo操作
我有一个执行多个Mongo操作的函数,最后一个操作是在所有其他操作完成后closures数据库。 我对这个问题的处理相当有信心,但是我有一些外部的意见引起了关注,我想validation我的解决scheme是否正确。
build议的解决scheme:
function updateDatabase (name, token) { return new Promise((resolve, reject) => { MongoClient.connect(MONGODB_URL) .then( (database) => { return database.collection('testCollection').update({name}, {$pull: {tokens: {$in: [token]}}}) .then( () => { database.collection('log').insert({ name, token }); return database; }) }) .then( (db) => { db.close(true); resolve(`Invalid token: ${token} has been removed from: ${name}`); }) .catch( (err) => { reject(err); }) }); }
我原来的解决scheme
function updateDatabase (name, token) { return new Promise((resolve, reject) => { MongoClient.connect(MONGODB_URL) .then( (database) => { return database; }) .then( (db) => { database.collection('testCollection').update({name}, {$pull: {tokens: {$in: [token]}}}) return database; }) .then( () => { database.collection('log').insert({ name, token }); return database; }) .then( (db) => { db.close(true); resolve(`Invalid token: ${token} has been removed from: ${name}`); }) .catch( (err) => { reject(err); }) }); }
我的原始解决scheme是不是商标或build议的解决scheme更好的方法? 两个工作在testing,但对生产等级的负载,我需要确保数据库不会closures,直到其他行动完成,我相信我已经完成了我原来的解决scheme。
由于MongoDB驱动程序的所有asynchronous操作都已经返回一个promise,所以不应该使用new Promise
,而是设置一个promise链:
function updateDatabase(name, token) { let database; return MongoClient.connect(MONGODB_URL).then(db => { database = db; return database .collection("testCollection") .update({ name }, { $pull: { tokens: { $in: [token] } } }); }) .then(() => { return database.collection("log").insert({ name, token }); }) .then(() => { database.close(true); }) .catch(err => { database.close(true); throw err; }); }
我知道你想传递database
作为下一个参数,但是你会遇到这个问题,它不会在catch
处理程序中可用。 一种解决scheme是使用一个函数范围的variables,在打开连接之后得到分配,就像上面的代码一样。
如果你不喜欢,你可以在MongoClient.connect
处理程序中创build一个新的promise链:
function updateDatabase(name, token) { return MongoClient.connect(MONGODB_URL).then(database => { return database .collection("testCollection") .update({ name }, { $pull: { tokens: { $in: [token] } } }) .then(() => { return database.collection("log").insert({ name, token }); }) .then(() => { database.close(true); }) .catch(err => { database.close(true); throw err; }); }); }
我试图概述原始代码的一些主要问题:
function updateDatabase (name, token) { //useless. MongoClient.connect already returns the promise return new Promise((resolve, reject) => { MongoClient.connect(MONGODB_URL) .then( (database) => { // useless. The function does nothing and can be removed return database; }) .then( (db) => { // should be db, not database // update is async. Should be added to the chain. database.collection('testCollection').update({name}, {$pull: {tokens: {$in: [token]}}}) // what's the point to return database, if the following function does not accept any parameters return database; }) .then( () => { // insert is async. Should be added to the chain. database.collection('log').insert({ name, token }); return database; }) .then( (db) => { // close is async. Should be added to the chain. db.close(true); resolve(`Invalid token: ${token} has been removed from: ${name}`); }) .catch( (err) => { reject(err); }) }); }
所以这个函数应该看起来像这样:
function updateDatabase (name, token) { return MongoClient.connect(MONGODB_URL) .then( db => db.collection('testCollection').update({name}, {$pull: {tokens: {$in: [token]}}}) .then(()=>db) }) .then( db => db.collection('log').insert({name, token}) .then(()=>db) }) .then( db => db.close(true)) .then(()=>`Invalid token: ${token} has been removed from: ${name}`); }
如果查询顺序无关紧要,您可以从Promise.all
:
function updateDatabase (name, token) { return MongoClient.connect(MONGODB_URL) .then( db => Promise.all([ Promise.resolve(db), db.collection('testCollection').update({name}, {$pull: {tokens: {$in: [token]}}}), db.collection('log').insert({name, token}), ]) .then( ([db]) => db.close(true)) .then(()=>`Invalid token: ${token} has been removed from: ${name}`); }
这两个解决scheme的共同问题是,然后(function)处理程序。 处理程序返回数据库,而不是为MongoDB操作返回Promise,因此将调用下一个链处理程序不等待MongoDB操作完成。 相反,承诺的MongoDB操作应该按照执行的顺序进行链接。 另外Mongo方法返回Promise,所以不需要新的Promise:
function updateDatabase (name, token) { return MongoClient.connect(MONGODB_URL) .then( (db) => Promise.all([ db.collection('testCollection').update({name}, {$pull: {tokens: {$in: [token]}}}), db.collection('log').insert({name, token}) ]).then(() => db.close(true) ).then(`Invalid token: ${token} has been removed from: ${name}` ).catch((err) => { db.close(true); throw err }); ) }
或者使用async / await语法:
async function updateDatabase (name, token) { let db = await MongoClient.connect(MONGODB_URL); try { await db.collection('testCollection').update({name}, {$pull: {tokens: {$in: [token]}}}); await db.collection('log').insert({name, token}); return `Invalid token: ${token} has been removed from: ${name}`; } finally { await db.close(true); } }