使用PostgreSQL构建带有逻辑的承诺链

时间:2016-09-26 00:40:30

标签: javascript postgresql express promise ecmascript-6

我很想承诺并试图在NodeJS上构建一个带有promise的逻辑。我有一个用于跟踪工作时间的API。

人们只需输入他们的密码即可输入或输出。

API(CLOCK-IN部分的)的作用是检查数据库(带有pg-promise的PostgreSQL)以查找带有PIN的员工ID。如果PIN无效(未找到员工),请拒绝承诺并显示错误消息。然后它必须查找他们的" ClockStatus"验证它们是否已经是IN或OUT。可以使用检索Employee ID的相同查询来完成。如果已经是IN,则拒绝带有消息的promise。然后,它必须从Employee中查找Shift ID。之后,它将收集Shift信息以比较时间。 Finnaly,if将在数据库中插入一行,其中包含上一步中收集的信息。

简而言之:

New Promise 
 Get the Employee ID and Clock Status, and Shift ID
Then
 If No Employee found then reject the promise
Then 
 If Clock Status is already in, reject the promise with a different message
Then
 Gather informations for the shift 
Then
 Insert a new row in the WorkHour table using Employee info and Shift informations

我的路由器在这里(使用ExpressJS)

router.post('/clockin', function(req, res) {
    //Execute the API
    time
        .clockin(req.body.pin)
        .then(function(time) { res.send(time)})
        .catch(function(err) { res.status(500).send(err) });
});

我试图建立承诺链,但我不想陷入厄运的困境中。

clockin: function(pin) {

    // Getting the resourceID from PIN
    var p = new Promise((resolve, reject) => {
        if (pin === undefined) {
            throw new Error('No PIN');
        } else {
            db.any('SELECT id, timeclockstatus From Employee Where PIN =  $1', pin)
            .then((EmployeeInfos) => {
                return (db.any('select id, timeclockstatus from res_resources where id = $1 ', ResourceID[0].id))
            })
//******  I'm stuck here

我想使用ES6的原生承诺。我试图查找示例,但似乎都没有适合我正在寻找的东西。或者也许我错过了一些东西...... 有什么建议吗?

2 个答案:

答案 0 :(得分:0)

以简化的形式,并非所有案例描述都清楚:

router.post('/clockin', function (req, res) {
    db.task(t=> {
        var pin = req.body.pin;
        if (!pin) {
            throw new Error('No PIN');
        }
        return t.one('SELECT id, timeclockstatus FROM Employee WHERE PIN = $1', pin)
            .then(data=> {
                // return another query request, based on `data`
            })
            .then(data=> {
                // return your insert query as needed, etc...
            });
    })
        .then(data=> {
            // provide successful response;
        })
        .catch(error=> {
            // provide error response;
        });
});

答案 1 :(得分:0)

如果你对承诺不熟悉,那么你可能会陷入困境并不奇怪。关键问题是了解how to access previous results in a then chain,特别是您需要最后阶段的员工信息(来自第一阶段)转移信息(来自第三阶段)。

正如您在参考文献中看到的,可以使用各种方法。在这些方法中:

  • "可变的上下文状态"是最简单但很难看的。
  • "打破链条"是一种可能性。
  • "嵌套和关闭"完全可行,但会给你一个你想要避免的厄运金字塔。
  • "明确的传递" (各种配方)很好地与Bluebird承诺一起制定,这将是我的选择。
clockin: function(pin) {
    if (!pin) {
        return Promise.reject(new Error('No PIN'));
    } else {
        // STAGE 1
        let sql = ....; // build sql for retrieving employee record
        return db.any(sql)
        .catch(e) => {
            throw new Error('problem finding employee info');
        }
        // STAGE 2
        .then((employeeInfo) => {
            let sql = ....; // build sql for retrieving employee's current status
            return Promise.all(employeeInfo, db.any(sql))
            .catch(e) => {
                throw new Error('problem finding current status');
            };
        })
        // STAGE 3
        .spread((employeeInfo, status) => {
            // a special case here, status === 'IN' requires us to throw.
            if (status === 'IN') {
                throw new Error('already IN');
            }
            let sql = ....; // build sql for gathering shift data
            return Promise.all(employeeInfo, status, db.any(sql))
            .catch(e) => {
                throw new Error('problem finding shift data');
            };
        })
        // STAGE 4
        .spread((employeeInfo, status, shiftData) => {
            let time = .....; // calculate `time` using all/any of `employeeInfo', 'status', 'shiftData`
            let sql = ...; // build sql for inserting new row in the WorkHour table.
            return db.any(sql)
            .then(() => [employeeInfo, status, shiftData, time]) // for completeness, make all data available to the caller, though you could return just `time`.
            .catch((e) => {
                throw new Error('problem saving new clocking');
            });
        });
    }
},

这四个阶段的表述略有不同,但性质相同。每个阶段:

  • 做了异步的事,
  • 将所有先前的结果以及自己的结果传递到主链的成功路径
  • 将有意义的错误消息传递到主链的错误路径中。

请注意使用Promise.all()和(Bluebird' s).spread()将多个结果提供给下一阶段。使用原生ES6承诺可以达到同样的效果,但上面展示的一些简单性将会丢失。

使用上面的代码,' / clockin'路由器功能将是:

router.post('/clockin', function(req, res) {
    //Execute the API
    time.clockin(req.body.pin)
    .spread((employeeInfo, status, shiftData, time) => { res.send(time); })
    .catch((err) => { res.status(500).send(err) }); // any uncaught errors thrown above will percolate down to here.
});