打破mongoose的蓝鸟诺言链

我已经研究了几个相关的问题和答案,仍然无法find我想要做的解决scheme。 我正在使用Mongoose和Bluebird来承诺。

我的承诺链包含3个部分:

  1. 通过用户名获取用户1

  2. 如果find用户1,则通过用户名获取用户2

  3. 如果find用户1和用户2,则存储新的logging

如果步骤1或步骤2无法返回用户,我不想执行步骤3.但是,无法返回用户不会导致数据库错误,因此我需要手动检查有效的用户。

我可以在步骤1中使用Promise.reject() ,它会跳过第2步,但仍然会执行第3步。其他答案build议使用cancel() ,但我似乎无法做到这一点。

我的代码如下。 (我的函数User.findByName()返回一个promise。)

 var fromU,toU; User.findByName('robfake').then((doc)=>{ if (doc){ fromU = doc; return User.findByName('bobbyfake'); } else { console.log('user1'); return Promise.reject('user1 not found'); } },(err)=>{ console.log(err); }).then((doc)=>{ if (doc){ toU = doc; var record = new LedgerRecord({ transactionDate: Date.now(), fromUser: fromU, toUser: toU, }); return record.save() } else { console.log('user2'); return Promise.reject('user2 not found'); } },(err)=>{ console.log(err); }).then((doc)=>{ if (doc){ console.log('saved'); } else { console.log('new record not saved') } },(err)=>{ console.log(err); }); 

所有你需要做的是这样的:

 let findUserOrFail = name => User.findByName(name).then(v => v || Promise.reject('not found')); Promise.all(['robfake', 'bobbyfake'].map(findUserOrFail)).then(users => { var record = new LedgerRecord({ transactionDate: Date.now(), fromUser: users[0], toUser: users[1], }); return record.save(); }).then(result => { // result of successful save }).catch(err => { // handle errors - both for users and for save }); 

更多信息

你可以创build一个函数:

 let findUserOrFail = name => User.findByName(name).then(v => v || Promise.reject('not found')); 

然后你可以像你想要的那样使用它。

比如你可以这样做:

 Promise.all([user1, user1].map(findUserOrFail)).then(users => { // you have both users }).catch(err => { // you don't have both users }); 

这样做会更快,因为您不必等待第一个用户获得第二个用户 – 两个用户都可以并行查询 – 您可以在将来将其扩展到更多的用户:

 let array = ['array', 'with', '20', 'users']; Promise.all(array.map(findUserOrFail)).then(users => { // you have all users }).catch(err => { // you don't have all users }); 

没有必要比这更复杂。

把你的error handling从内部链中移出到你想要实际捕获/处理的地方。 由于我没有安装mongo,这里有一些伪代码应该做的伎俩:

 function findUser1(){ return Promise.resolve({ user: 1 }); } function findUser2(){ return Promise.resolve({ user: 2 }); } function createRecord(user1, user2){ return Promise.resolve({ fromUser: user1, toUser: user2, }); } findUser1() .then(user1 => findUser2() .then(user2 => createRecord(user1, user2))) // better nest your promises as having variables in your outside scope .then(record => console.log('record created')) .catch(err => console.log(err)); // error is passed to here, every then chain until here gets ignored 

尝试通过更改find​​User1来

 return Promise.reject('not found 1'); 

首先,我会推荐使用throw x; 而不是return Promise.reject(x); ,只是为了可读性的原因。 其次,你的错误logging函数可以捕获所有错误,这就是为什么你的承诺链正在继续。 尝试重新抛出错误:

 console.log(err); throw err; 

不要把错误logging放在任何地方,而不是实际处理错误 – 如果你传递一个error handling函数callback函数,你将会得到一个将会以undefined实现的promise,这不是你所需要的。 只是使用

 User.findByName('robfake').then(fromUser => { if (fromUser) { return User.findByName('bobbyfake').then(toUser => { if (toUser) { var record = new LedgerRecord({ transactionDate: Date.now(), fromUser, toUser }); return record.save() } else { console.log('user2 not found'); } }); } else { console.log('user1 not found'); } }).then(doc => { if (doc) { console.log('saved', doc); } else { console.log('saved nothing') } }, err => { console.error("something really bad happened somewhere in the chain", err); }); 

这将始终logging其中一个“已保存”或“不好的”消息,可能还会有一个“未find”消息。

你也可以使用exception来达到这个目的,但这并不简单:

 var user1 = User.findByName('robfake').then(fromUser => { if (fromUser) return fromUser; else throw new Error('user1 not found'); }); var user2 = user1.then(() => // omit this if you want them to be searched in parallel User.findByName('bobbyfake').then(toUser => { if (toUser) return toUser; else throw new Error('user2 not found'); }) ); Promise.all([user1, user2]).then([fromUser, toUser]) => var record = new LedgerRecord({ transactionDate: Date.now(), fromUser, toUser }); return record.save(); }).then(doc => { if (doc) { console.log('saved', doc); } else { console.log('saved nothing') } }, err => { console.error(err.message); });