使用bluebird管理顺序流中的异步操作

时间:2016-09-28 02:14:30

标签: javascript promise bluebird

首先,我不会否认我对承诺很新,并试图在我的新Node.js应用程序中更好地管理承诺。我根据朋友和社区的意见使用蓝鸟。这是一个场景:

该应用有一个注册流程,这是一个典型的用例,必须在以下事件中注册才能注册新用户:

  1. 检查用户是否已存在。
  2. 如果没有,请添加新用户。
  3. 发送验证邮件。
  4. 我确实有3个独立的功能来解决上述每个步骤。

    现在这里是我使用promises flow得出的结果......但不知何故我不相信下面的代码:

    user.isExistingUser(email)
            .then((successData) => {
                if(successData && successData.length === 0) {
                    user.signUp(signUpInfo)
                    .then((successData) => {
                        emailService.sendVerificationEmail(recipientInfo)
                        .then((successData) => {
                         res.json(responseUtility.getApiResponse(successData));
                        })
                        .catch((errorObj) => {
                            res.json(responseUtility.getApiResponse(null, null, errorObj));
                        });
                    })
                    .catch((errorObj) => {
                        res.json(responseUtility.getApiResponse(null, null, errorObj));
                    });
                } else {
                    res.json(responseUtility.getApiResponse(null, [{
                        param: 'email',
                        msg: 'An account already exists with this email'
                    }], null));
                }
            })
            .catch((errorObj) => {
                res.json(responseUtility.getApiResponse(null, null, errorObj));
            });
    

    正如您可能会看到代码似乎有点太长而且跟踪可能会变得有点棘手。一些蓝鸟专家可以帮助提供更好或更易读的代码吗?

3 个答案:

答案 0 :(得分:2)

你应该更好地利用链接。始终return承诺从您的函数执行异步操作。

user.isExistingUser(email).then(successData => {
    if (successData && successData.length === 0) {
        return user.signUp(signUpInfo).then(() => {
//      ^^^^^^
            return emailService.sendVerificationEmail(recipientInfo);
//          ^^^^^^
        }).then(successData => {
            res.json(responseUtility.getApiResponse(successData));
        });
    } else {
        res.json(responseUtility.getApiResponse(null, [{
            param: 'email',
            msg: 'An account already exists with this email'
        }], null));
    }
}).catch(errorObj => {
    res.json(responseUtility.getApiResponse(null, null, errorObj));
});

答案 1 :(得分:1)

Unserstand您的代码有什么问题

基本上,您的代码是嵌套回调。典型的嵌套回调如下:

funcation(args.., 
    function(args..., 
        function(args){...}
    ){...}
){...}

你的喜欢:

function(args).then(()=> {
   function(args).then(() => {
       function(args){...}
   })
})

差别不大,不是吗?

这是使用Promise的正确方法。

解决问题的关键是将承诺链接起来。这实际上是承诺的全部要点。

基本上,您希望then回调返回一个承诺。这样,您可以链接您的承诺,以避免嵌套的回调样式的代码。

每个.then方法总是返回一个承诺本身。

promiseB = promiseA.then(()=> {
    // callback 
    return promiseC
});

如果callback返回上述承诺,您可以有效地考虑promiseB = promiseC。现在,如果我们有

promiseC = promiseA.then(()=> {
    // callback 
    return promiseB
});
promiseE = promiseC.then(()=> {
    // callback 
    return promiseD
});

您可以链接在一起。然后上面可以缩短为:

promiseA.then(()=> {
    // callback 
    return promiseB
}).then(()=> {
    // callback 
    return promiseD
});

直接回答您的问题。

let userPromise = user.isExistingUser(email); 

// handle new user
userPromise
.then((userData) => {
  assert userData.length === 0;
  // sign up
  // Assuming user.signUp is a Promise with recipientInfo as its resolved value
  return user.signUp(userData.signUpInfo); 
})
.then((recipientInfo)=> {
  // send verification email
  // Assuming user.sentVerificationEmail is a Promise with emailSuccessData as its resolved value
  return user.sentVerificationEmail(recipientInfo)
})
.then((emailSuccessData)=> {
  res.json(responseUtility.getApiResponse(emailSuccessData));
})
// if any of the previous Promise is rejected, 
// the follow .then will be effectively bypassed and fall though to this catch
.catch((err)=> {
  res.json(responseUtility.getApiResponse(null, null, err));
})


// handle existing user
userPromise.then((successData) => {
  assert successData.length > 0
  const json = [{ 
    param: 'email', 
    msg: 'An account already exists with this email'
  }]
  res.json(responseUtility.getApiResponse(null, json, null));
}); 
// no need to catch again. if userPromise is rejected, it will be handled by the catch above.

答案 2 :(得分:0)

我想一种实现相同代码的合理方式可能就像;

user.isExistingUser(email)
    .then(successData => successData &&
                         successData.length === 0 ? user.signUp(signUpInfo)
                                                        .then(successData => emailService.sendVerificationEmail(recipientInfo))
                                                        .then(successData => res.json(responseUtility.getApiResponse(successData)))
                                                  :  res.json(responseUtility.getApiResponse(null, [{param: 'email', msg: 'An account already exists with this email'}], null)))
    .catch(err => res.json(responseUtility.getApiResponse(null, null, err)));