承诺地狱,反模式和错误处理

时间:2018-04-15 17:44:36

标签: javascript node.js es6-promise

我是node.js环境中的新手。我阅读了许多关于实现Promise并将它们链接在一起的来源。我试图避免反模式实现,但我无法弄明白我该怎么做。

系统中有用户注册流程。 首先,我检查用户名。 如果数据库中没有此用户名的用户,我创建一个用户模型并将其保存到数据库中。

你能否看到我的评论内联?

app.js

RegisterUser("existUser","123").then(user=>{
    //send response, etc
}).catch(er => {
    console.log(er);
    //log error,send proper response, etc
    // Should I catch error here or inner scope where RegisterUser implemented ?
});

userService.js

function RegisterUser(username, password) {
    return new Promise((resolve, reject) => {
        GetUser(username) 
            .then(user=>{
                if(user)reject(new Error("User Exists"));
                else{
                    resolve(SaveUser(username,password))// Is that ugly?
                                                        //what if I have one more repository logic here ?
                                                        //callback train... 
                }
            }) 
            .then(user => {
                resolve(user);//If I do not want to resolve anything what is the best practice for it, like void functions?
            }).catch(err=>{
                console.log(err); // I catch the db error what will I do now :)
                reject(err);// It seems not good way to handle it, isn't it ?
                // Upper promise will handle that too. But I dont know how can I deal with that.
            });;
    });
}

repository.js

function GetUser(username) {
    return new Promise((resolve, reject) => {
        if (username === "existUser")
            resolve("existUser");
        else resolve("");
    });
}

function SaveUser(username, password) {
    return new Promise((resolve, reject) => {
        reject(new Error("There is a problem with db"));//assume we forgot to run mongod 
    });
}

上面的代码对我来说似乎很糟糕。 我想我需要定义一些可以在GetUser方法之后链接的方法。 像

GetUser(username)
.then(SaveUserRefined)// how could it know other parameters like password, etc
.then(user=> {resolve(user)}) // resolve inside then ? confusing.
.catch(er=>//...);

我觉得我在这里做反模式并且创造了#34;承诺地狱" 如何实现这样的简单流程。 验证用户名并保存。

感谢。

3 个答案:

答案 0 :(得分:4)

是的,那是Promise constructor antipattern!只需写下

function registerUser(username, password) {
    return getUser(username).then(user => {
        if (user) throw new Error("User Exists");
        else return saveUser(username,password)
    });
}

注意我还小写了你的函数名。您应该只将构造函数大写。

  

我应该在这里发现错误还是在registerUser实施的内部范围?

您应该在可以处理它们的地方捕获错误 - 并且您应该在链的末尾处理错误(通常通过记录它们)。如果registerUser没有提供回退结果,则不需要处理任何内容,并且通常也不需要自己记录错误消息(如果您愿意,请参阅{{3}例如)。

另见here

答案 1 :(得分:3)

如果你已经在使用promises,那么就没有必要创建自己的promises。当你在一个promise上调用.then并提供一个函数来说明该做什么时,会创建一个新的promise,它将解析为函数返回的任何内容。

所以你的registerUser函数应该是这样的:

function registerUser(username, password) {
  return getUser(username)
    .then(user => {
      if (user) {
        throw new Error('User Exists');
      }
      return saveUser(username, password)
    });
}

你可以这样使用它:

registerUser('starfox', 'doABarrelRoll')
  .catch(error => console.log(error);

请注意,如果SaveUser导致错误,它将在最终的.catch中结束,因为我没有在registerUser中放置任何错误处理。由您决定要处理错误的位置。如果它是可恢复的东西,也许registerUser可以处理它,外面的世界永远不需要知道。但是,如果用户名存在,您已经抛出错误,因此调用者无论如何都需要知道错误。

此外,你的getUser和saveUser函数也可以避免创建自己的promises,假设真正的实现调用了一些返回promise的函数。

答案 2 :(得分:0)

您应该使用async/await语法以避免Promise Hell。

像这样更改代码

/**
 * This is a promise that resolve a user or reject if GetUser or SaveUser reject
 */
async function RegisterUser (username, password) { 
    var user = await GetUser(username)

    if (user)
        return user;

    var userSaved = await SaveUser(username,password)
    return userSaved
}

如果您在RegisterUser内使用async function,只需进行编码

async function foo () {
    try {
        var usr = await RegisterUser('carlos', 'secret123')
        return usr
    } catch (e) {
        console.error('some error', e)
        return null
    }
}

或者如果您像诺言般使用它

RegisterUser('carlos', 'secret123')
    .then(usr => console.log('goood', usr))
    .catch(e => console.error('some error', e))