简体   繁体   English

摆脱猫鼬的蓝鸟承诺链

[英]Break out of Bluebird promise chain in Mongoose

I've studied several related questions & answers and still can't find the solution for what I'm trying to do. 我已经研究了几个相关的问题和答案,但仍然找不到我要解决的问题的解决方案。 I'm using Mongoose with Bluebird for promises. 我正在将猫鼬和蓝鸟一起使用来承诺。

My promise chain involves 3 parts: 我的诺言链包括三个部分:

  1. Get user 1 by username 通过用户名获取用户1

  2. If user 1 was found, get user 2 by username 如果找到用户1,请通过用户名获取用户2

  3. If both user 1 and user 2 were found, store a new record 如果同时找到用户1和用户2,则存储新记录

If either step 1 or step 2 fail to return a user, I don't want to do step 3. Failing to return a user, however, does not cause a database error, so I need to check for a valid user manually. 如果第1步或第2步都无法返回用户,则我不想执行第3步。但是,未能返回用户不会导致数据库错误,因此我需要手动检查有效用户。

I can use Promise.reject() in step 1 and it will skip step 2, but will still execute step 3. Other answers suggest using cancel() , but I can't seem to make that work either. 我可以在步骤1中使用Promise.reject() ,它将跳过步骤2,但仍将执行步骤3。其他答案建议使用cancel() ,但我似乎也无法执行。

My code is below. 我的代码如下。 (My function User.findByName() returns a promise.) (我的函数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);
    });

Example

All you need to do is something like this: 您需要做的就是这样:

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
});

More info 更多信息

You can create a function: 您可以创建一个函数:

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

and then you can use it like you want. 然后您可以根据需要使用它。

Eg you can do: 例如,您可以这样做:

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

That way will be faster because you don't have to wait for the first user to get the second one - both can be queried in parallel - and you can scale it to more users in the future: 这种方式将更快,因为您不必等待第一个用户获得第二个用户-可以并行查询两个用户-并且将来可以将其扩展到更多用户:

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
});

No need to complicate it more than that. 无需使它复杂化。

move your error handling out of the inner chain to the place you want to actual catch/handle it. 将错误处理从内链移到要实际捕获/处理的位置。 As i don't have mongo installed, here is some pseudocode that should do the trick: 由于我没有安装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

Try it by changing findUser1 to 通过将findUser1更改为

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

First, I would recommend using throw x; 首先,我建议使用throw x; instead of return Promise.reject(x); 而不是return Promise.reject(x); , simply for readibility reasons. ,仅出于可读性原因。 Second, your error logging functions catch all the errors, that's why your promise chain is continuing. 其次,您的错误记录功能捕获了所有错误,这就是您的承诺链持续不断的原因。 Try rethrowing the errors: 尝试重新抛出错误:

console.log(err);
throw err;

Don't put error logging everywhere without actually handling the error - if you pass an error handler callback you'll get back a promise that will fulfill with undefined , which is not what you can need. 不要在没有实际处理错误的情况下将错误记录放到任何地方-如果传递错误处理程序回调,您将获得一个承诺,该承诺将使用undefined来实现,这不是您所需要的。 Just use 只需使用

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);
});

This will always log one of the "saved" or "something bad" messages, and possibly one of the "not found" messages before. 这将始终记录“已保存”或“某事不好”消息之一,并且可能之前记录“未找到”消息之一。

You can also use exceptions to achieve this, but it doesn't really get simpler: 您也可以使用异常来实现此目的,但实际上并没有变得更简单:

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);
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM