【问题标题】:(NestJS, TypeORM) Javascript promises : Finally block is called before then(NestJS, TypeORM) Javascript 承诺:Finally 块在此之前被调用
【发布时间】:2021-12-12 14:04:00
【问题描述】:

我正在为用户资源创建一个带有 CRUD 操作的 API。因为 TypeORM 在将创建的用户插入数据库后不会返回它,所以我执行了一个“findOne”请求以提供用户名来取回它。

为了做到这一点,我使用 QueryRunner 在我的 UserService 中创建了一个事务。它看起来像这样:

控制器

@Post()
  create(@Body() user: User) {
    return this.userService
      .createUser(user)
      .then((user) => {
        return user;
      })
      .catch(() => {
        throw new InternalServerErrorException('Could not create user');
      });
  }

服务

createUser(user: User): Promise<User | void> {
    const queryRunner = this.connection.createQueryRunner();

    return queryRunner
      .connect()
      .then(() => {
        queryRunner
          .startTransaction()
          .then(() => {
            queryRunner.manager
              .save(User, user)
              .then((user) => {
                queryRunner.manager
                  .findOne(User, {
                    username: user.username,
                  })
                  .then((user) => {
                    queryRunner
                      .commitTransaction()
                      .then(() => {
                        return Promise.resolve(user);
                      })
                      .catch((error) => {
                        console.log('Could not commit transaction : ', error);
                        return Promise.reject();
                      });
                  })
                  .catch((error) => {
                    console.log(
                      `Could not get user after insert, username : ${user.username}, error : ${error}`,
                    );
                    return Promise.reject();
                  });
              })
              .catch((error) => {
                console.log(
                  'Could not insert user into the database : ',
                  error,
                );
                queryRunner
                  .rollbackTransaction()
                  .then(() => {
                    console.log('Rolled back transaction');
                    return Promise.resolve();
                  })
                  .catch((error) => {
                    console.log('Could not rollback transaction : ', error);
                    return Promise.reject();
                  });
              });
          })
          .catch((error) => {
            console.log('Could not start transaction : ', error);
            return Promise.reject();
          });
      })
      .catch((error) => {
        console.log('Could not connect to database : ', error);
        return Promise.reject();
      })
      .finally(() => {
        queryRunner
          .release()
          .then((user) => {
            console.log('Released query runner for User transaction');
            return Promise.resolve(user);
          })
          .catch((error) => {
            console.log('Could not release queryRunner : ', error);
            return Promise.reject();
          });
      });
  }

这段代码有很多问题,我知道,但我是 javascript 世界的新手,不知道如何做得更好:

  1. 我查看了互联网,有人将我正在做的事情(嵌套承诺)称为末日金字塔,这是一种反模式,但我不知道该怎么做。
  2. 当我运行我的代码并请求创建用户时,finally() 块在 then() 之前被调用,因此在我执行我的事务之前释放我的 queryRunner。谁能告诉我为什么?
  3. 为了解决这个问题,我删除了 finally() 并在 then() 和 catch() 中进行了释放,但后来发生的事情是我的函数在调用“startTransaction”后返回并且没有执行 then()之前,所以发生的情况是我在完成我的 UserService 函数 (createUser) 之前返回控制器并向用户返回响应。

我希望您有足够的信息来帮助我,但如果需要,我很乐意添加更多详细信息。

谢谢,

【问题讨论】:

  • 您通常可以通过使用链式 Promise 来避免嵌套的 Promise,或者您也可以切换到 async/await 语法。

标签: javascript typescript promise nestjs typeorm


【解决方案1】:

关于您的问题

1- 尝试将您的函数转换为 async 并使用 async/await 这将使您更清楚地了解您尝试实现的逻辑

2- 在then 之前调用finally 可能意味着发生了错误,尝试在catch 中记录错误并进行调试

这不是答案,我只是向你展示了一种替代承诺金字塔的方法,我希望你的逻辑是正确的

async function createUser(user): Promise<any> {
    const queryRunner =  this.connection.createQueryRunner();

    await queryRunner.connect().catch(throwErr("couldn't connect") )

    await  queryRunner.startTransaction().catch(throwErr('could not start transaction'))

    const  User = await queryRunner.manager.save(user).catch(throwErr("could not save user") )

    await queryRunner.commitTransaction().catch(throwErr("could not complete transaction"))

    await queryRunner.release().catch(throwErr('Could not release queryRunner'))

    const foundedUser = await queryRunner.manager.findOne(User, {
        username: user.username,
    }).catch(throwErr("Could not connect to database"))

    return foundedUser
}

function throwErr(message){
    return (err)=> {throw new Error(message)}
}

【讨论】:

  • 感谢您的帮助,我从嵌套的 Promise 更改为 async/await,它变得更好,更易读。我只是想指出,经过调查,即使没有错误并且 catch 也没有触发,最终还是运行了。我将发布我的新代码的样子。
【解决方案2】:

感谢 scr2em 和 David Callanan,我将代码从嵌套 promise 更改为 async/await,现在它可以工作了,这就是我所做的:

async createUser(user: User): Promise<User | void> {
    const action = `(Create user : ' ${user.username}) `;
    const queryRunner = this.connection.createQueryRunner();

    await queryRunner.connect().catch((error) => {
      console.log(action + 'Could not connect to database : ', error);
      return Promise.reject();
    });
    await queryRunner.startTransaction().catch((error) => {
      console.log(action + 'Could not start transaction : ', error);
      return Promise.reject();
    });
    await queryRunner.manager.save(User, user).catch(async (error) => {
      console.log(action + 'Could not insert user into the database : ', error);
      await queryRunner.rollbackTransaction().catch((error) => {
        console.log(action + 'Could not rollback transaction : ', error);
        return Promise.reject();
      });
      return Promise.reject();
    });
    const userResult = await queryRunner.manager
      .findOne(User, { username: user.username })
      .catch(async (error) => {
        console.log(
          action +
            `Could not get user after insert, username : ${user.username}, error : ${error}`,
        );
        await queryRunner.rollbackTransaction().catch((error) => {
          console.log(action + 'Could not rollback transaction : ', error);
          return Promise.reject();
        });
        return Promise.reject();
      });
    await queryRunner.commitTransaction().catch((error) => {
      console.log(action + 'Could not commit transaction : ', error);
      return Promise.reject();
    });
    await queryRunner.release().catch((error) => {
      console.log(action + 'Could not release queryRunner : ', error);
      return Promise.reject();
    });
    return Promise.resolve(userResult);
  }

希望对其他人有所帮助。

【讨论】:

    猜你喜欢
    • 2020-08-28
    • 2014-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多