node.js - 如何优化 Promise 以避免回调/Promise hell

标签 node.js express promise callback knex.js

我的数据库使用 knex.js,并且我有一个依赖于上一个查询的查询。

示例:

用户表

|用户名(pk) |名字 |姓氏 |

登录表

|用户名(pk/fk) |哈希 |

流程是:

插入用户 > 插入登录

登录依赖于用户,因此如果尚未完成对用户的插入,则会返回错误。

这是我的代码:

const handleSignup = (req, res, db, logger, bcrypt) => {
    const {
        username,
        password,
        firstName,
        lastName,
    } = req.body;
    const hash = bcrypt.hashSync(password);
    if (username || !firstName || !lastName ) {
        res.json({
            haveEmpty: true
        });
        return;
    } else {
        db.transaction((trx) => {
                db.select('*').from('user').where('username', '=', username)
                    .then(data => {
                        if (!data[0]) {
                            db('user')
                                .returning('*')
                                .insert({
                                    username: username,
                                    first_name: firstName,
                                    last_name: lastName,
                                })
                                .then(user => {
                                    db('login')
                                        .returning('*')
                                        .insert({
                                            username: username,
                                            hash: hash
                                        })
                                        .then(login => {
                                            if (login[0]) {
                                                res.json({
                                                    isSuccess: true
                                                });
                                                return;
                                            } else {
                                                res.json({
                                                    isSuccess: false
                                                });
                                                return;
                                            }
                                        })
                                        .then(trx.commit)
                                        .catch(err => {
                                            logger.error(err);
                                            trx.rollback;
                                            res.render('pages/error-500');
                                        });
                                })
                                .then(trx.commit)
                                .catch(err => {
                                    logger.error(err);
                                    trx.rollback;
                                    res.render('pages/error-500');
                                });
                        } else {
                            res.json('User already Exist!');
                            return;
                        }
                    })
                    .then(trx.commit)
                    .catch(err => {
                        logger.error(err);
                        trx.rollback;
                        res.render('pages/error-500');
                    });
            })
            .catch(err => logger.error(err));
    }
}

而且我不知道我是否正确使用了交易。但这就是我想到的。之前,当我将查询分成两个 promise 时,我收到错误,因为似乎第一个插入(用户)未完成。

此代码可以正常工作,但我知道有更正确的编码方法。

最佳答案

根据我的经验,一旦你不再试图将所有 promise 都塞进同一个函数中, promise 就会变得更加自然! (但是我们所有人都可能曾经写过与您的示例类似的内容,请不要担心。)

较小的代码块往往也更容易测试和调试。例如,如果您知道对请求正文中的变量的检查是正确的,那么问题可能出在堆栈的更深处。

这是一个使用小型中间件堆栈的示例。这允许将操作分解为小块,同时仍然保证一件事会在另一件事之前发生。

const bcrypt = require("bcrypt");
const express = require("express");
const knex = require("knex");
const config = require("./knexfile").development;

const app = express();
app.use(express.json());
const db = knex(config);

const detailValidator = (req, res, next) => {
  // You can do more robust validation here, of course
  if (!req.body.firstName || !req.body.lastName) {
    return next(new Error("Missing user details."));
  }
  next();
};

const userUniqueValidator = (req, res, next) => {
  db("users")
    .where("username", req.body.username)
    .then(users => {
      if (users.length !== 0) {
        return next(new Error("User exists."));
      }
      next();
    });
};

const userCreator = (req, res, next) => {
  const { username, password, firstName, lastName } = req.body;
  const hash = bcrypt.hashSync(password, 10);

  db.transaction(trx =>
    trx("users")
      .insert({
        username,
        first_name: firstName,
        last_name: lastName
      })
      .then(([userId]) => trx("auth").insert({ user_id: userId, hash }))
      .then(() => res.json({ success: true }))
  ).catch(err => next(err));
};

app.post("/", detailValidator, userUniqueValidator, userCreator);

app.use((err, req, res, next) => res.json({ error: err.message }));

app.listen(4000, () => console.log("yup"));

关于 Knex 中的事务:如果使用上述语法,您实际上根本不需要调用 commit。但是,您确实需要使用 trx 参数作为查询构建器。该文档还建议了另一个选项,即 transacting 语法:请参阅 docs .

最后,我真的不建议使用您的用户名作为主键。它们经常需要更改,而且总是存在意外泄漏 URL 或日志的风险。不过,我建议包括一个独特的约束。也许是这样的?

exports.up = knex =>
  knex.schema.createTable("users", t => {
    t.increments("id");
    t.string("username").unique();
    t.string("first_name");
    t.string("last_name");
  });

exports.up = knex =>
  knex.schema.createTable("auth", t => {
    t.increments("id");
    t.integer("user_id").references("users.id");
    t.string("hash");
  });

值得注意的是,我在这个快速示例中使用了 SQLite3,它仅支持在插入后返回行 id(因此在用户插入后 then 子句中使用 [ userId ])。

关于node.js - 如何优化 Promise 以避免回调/Promise hell ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55912652/

相关文章:

javascript - 按顺序排列的多个 jQuery promise

node.js - Mongoose:如何在实例方法中访问实例的属性

javascript - 单个解析器/ promise 的多个生产者

node.js - Passport.js 登录事件函数未被调用

javascript - 我如何使用 Mongoose 循环迭代 Mongoose 返回的文档数组?

model-view-controller - Node.JS/Mongoose/Express -> 对象没有方法 "findAll"

Node.js 集群模块似乎破坏了 Socket.io 握手

javascript - Discord.js 如何制作建议命令?

javascript - 循环中的异步函数 - 使变量在内部函数中可用

javascript - 如何捕获数组中所有 Promise 都得到解决的事件?