在节点js中正确使用异步等待

时间:2017-12-04 09:34:41

标签: node.js express async-await refactoring

为了克服javascript中的回调地狱,我正在尝试使用SQLServer过程中编写的遗留代码进行异步等待。 但我不确定我的代码是否可以写得正确。

我的第一个令人困惑的地方是异步函数返回时,它应该将resolve()作为boolean返回,还是仅使用try-catch返回reject和handle?

这是我的代码片段。 请纠正我正确的方向。

apiRoutes.js

app.route('/api/dansok/cancelDansok')
    .post(dansokCancelHandler.cancelDansok);

dansokCancelController.js

const sequelize = models.Sequelize;
const jwt = require('jsonwebtoken');

async function jwtAccessAuthCheck(accessToken) {
  if (!accessToken) {
    return Promise.reject('Empty access token');
  }

  jwt.verify(accessToken,"dipa",function(err){
    if(err) {
      return Promise.reject('TokenExpiredError.');
    } else {
      return Promise.resolve();
    }
  });
}

async function checkFeeHist(dansokSeqNo) {
  let feeHist = await models.FeeHist.findOne({  
                  where: { DansokSeqNo: dansokSeqNo}
                });
  return !!feeHist;
}

async function getNextDansokHistSerialNo(dansokSeqNo) {
  ....
}

async function getDansokFee(dansokSeqNo) {
  ....
}

async function doCancel(dansokSeqNo) {
  try {
    if (await !checkFeeHist(dansokSeqNo)) {
      log.error("doCancel() invalid dansokSeqNo for cancel, ", dansokSeqNo);
      return;
    }
    let nextDansokSerialNo =  await getNextDansokHistSerialNo(dansokSeqNo);
    await insertNewDansokHist(dansokSeqNo, nextDansokSerialNo);
    await updateDansokHist(dansokSeqNo);
    await updateVBankList(dansokSeqNo, danokFee.VBankSeqNo);
    await getVBankList(dansokSeqNo);
  } catch (e) {
    log.error("doCancel() exception:", e);
  }
}

exports.cancelDansok = function (req, res) {
  res.setHeader("Content-Type", "application/json; charset=utf-8");
  const dansokSeqNo = req.body.DANSOKSEQNO;
  const discKindCode = req.body.HISTKIND;
  const worker = req.body.PROCWORKER;
  const workerIp = req.body.CREATEIP;
  const accessToken = req.headers.accesstoken;

  //check input parameter
  if (!dansokSeqNo || !discKindCode || !worker || !workerIp) {
    let e = {status:400, message:'params are empty.'};
    return res.status(e.status).json(e);
  } 

  try {
    jwtAccessAuthCheck(accessToken)
    .then(() => {
      log.info("jwt success");
      doCancel(dansokSeqNo).then(() => {
        log.info("cancelDansok() finish");
        res.status(200).json({ message: 'cancelDansok success.' });
      });
    });
  } catch(e) {
    return res.status(e.status).json(e);
  }
};

1 个答案:

答案 0 :(得分:1)

您需要重写 jwtAccessAuthCheck(accessToken),以便跟踪其嵌套任务的结果。在您编写的代码中:

// Code that needs fixes!
async function jwtAccessAuthCheck(accessToken) {

  // This part is fine. We are in the main async flow.
  if (!accessToken) {
    return Promise.reject('Empty access token');
  }

  // This needs to be rewritten, as the async function itself doesn't know anything about
  // the outcome of `jwt.verify`... 
  jwt.verify(accessToken,"dipa",function(err){
    if(err) {
      // This is wrapped in a `function(err)` callback, so the return value is irrelevant
      // to the async function itself
      return Promise.reject('TokenExpiredError.');
    } else {
      // Same problem here.
      return Promise.resolve();
    }
  });

  // Since the main async scope didn't handle anything related to `jwt.verify`, the content
  // below will print even before `jwt.verify()` completes! And the async call will be
  // considered complete right away.
  console.log('Completed before jwt.verify() outcome');

}

更好的重写是:

// Fixed code. The outcome of `jwt.verify` is explicitly delegated back to a new Promise's
// `resolve` and `reject` handlers, Promise which we await for.
async function jwtAccessAuthCheck(accessToken) {
  await new Promise((resolve, reject) => {

    if (!accessToken) {
      reject('Empty access token');
      return;
    }

    jwt.verify(accessToken,"dipa",function(err){
      if(err) {
        reject('TokenExpiredError.');
      } else {
        resolve();
      }
    });

  });

  // We won't consider this async call done until the Promise above completes.
  console.log('Completed');

}

在此特定用例中也可以使用的备用签名:

// Also works this way without the `async` type:
function jwtAccessAuthCheck(accessToken) {
  return new Promise((resolve, reject) => {
    ...
  });
}

关于您的cancelDansok(req, res)中间件,由于jwtAccessAuthCheck保证返回Promise(您使其成为异步函数),因此您还需要直接处理其返回的Promise。没有try / catch可以处理这个异步任务的结果。

exports.cancelDansok = function (req, res) {

  ...

  jwtAccessAuthCheck(accessToken)
    .then(() => {
      log.info("jwt success");
      return doCancel(dansokSeqNo);
    })
    .then(() => {
      log.info("cancelDansok() finish");
      res.status(200).json({ message: 'cancelDansok success.' });
    })
    .catch(e => {
      res.status(e.status).json(e);
    });

};

我强烈建议您阅读一些与Promise相关的文章,以便掌握它。它们非常方便和强大,但与其他JS模式(异步回调,try / catch ...)混合时也带来一点痛苦。