【问题标题】:Nodejs promise all not running as expectedNodejs承诺一切都没有按预期运行
【发布时间】:2017-11-16 09:36:42
【问题描述】:

我在testCard 中绑定了一系列承诺。此方法采用stripe 卡号,从条带获取令牌,然后与尝试使用该卡执行购买的第三方 API 对话。 我需要通过循环卡号数组来运行testCard。为此,我有一个 controller 对象,其方法 testAllCards 采用数字数组。该数组存储在配置文件中。

然后我从命令行使用node cli.js testAllCards 运行代码。

但是,当我运行它时,在所有大多数承诺都解决之前,我得到了testAllCards has been run。 我显然在这里遗漏了一些东西,但似乎无法弄清楚它是什么。

cli.js

const testAllCards = () => {
  return controller.testAllCards(config.get('CARD_NUMBERS'))
    .then((obj) => {
      console.log('testAllCards has been run');
    })
    .catch((e) => {
      console.log('testCards has been run with an error!');
      const _err = new ErrHandler(e, eTopicName, eSnsSubject);
      _err.handle()
        .then(() => {
          console.log('Error has been sent with success to sns');
        });
    });
};

switch(process.argv[2]) {
  case 'testAllCards':
    testAllCards();
    break;
  default:
    console.log('Please run with `testAllCards`');

controller.js

//Tests response from API for different cards
const testCard = (cardNum) => {
  return new Promise((resolve, reject) => {
    const expMonth = new Date().getMonth() + 1;
    const expYear = new Date().getFullYear() + 2;
    const cardObj = {
      cardNum: cardNum,
      expMonth: expMonth,
      expYear: expYear
    };
    let apiCardItem = '';
    return testRequestToApi('getStripeToken', 200, 299, cardObj)
      .then((cardItem) => {
        return testRequestToApi('postNewCard', 200, 299, JSON.parse(cardItem.body));
      })
      .then((apiCard) => {
        apiCardItem = apiCard.body;
        try {
          apiCardItem = JSON.parse(apiCardItem);
        } catch(e) {
          console.log(e);
        }
        return testRequestToApi('sampleAddToCart', 200, 299);
      })
      .then(() => {
        return testRequestToApi('useFailingStripeCards', 400, 499, apiCardItem.id);
      })
      .then(() => {
        return testRequestToApi('deleteCard', 200, 299, apiCardItem.id);
      })
      .then(() => {
        resolve();
      })
      .catch((e) => {
        reject(e);
      });
  });
};

//Loops through the card numbers and runs the test command against them
Controller.testAllCards = (cardsArray) => {
  const items = cardsArray.map((cardNum) => {
    return testCard(cardNum);
  });
  return Promise.all(items);
};

module.exports = Controller;

test-request-to-api.js

'use strict';

const checkStatus = require('./../utils/status-code-checker');
const formHeaders = require('./../utils/form-req-headers');
const request     = require('request');
const expObj = {};

//@requestType {string} - defines which headers and function name to use
//@item {object} - defines item that is being used
expObj.testRequestToApi = (requestType, lowerLimit, upperLimit, item) => {
  return new Promise((resolve, reject) => {
    const reqOps = formHeaders[requestType](item);
    request(reqOps, (err, response, body) => {
      if (err) {
        const badRequest = {
          ErrorMessage: err,
          FuncName: requestType,
          InternalError: true
        };
        return reject(badRequest);
      }
      if (!checkStatus.checkRangeStatusCode(response.statusCode, lowerLimit, upperLimit)) {
        console.log(JSON.stringify(body, null, 2));
        // Set a bad Status error object
        let badStatus = {
          StatusCode: response.statusCode,
          ErrorMessage: body,
          FuncName: requestType,
          InternalError: false
        };
        return reject(badStatus);
      }
      // console.log(response.headers);
      // console.log(body);
      const resObj = {
        headers: response.headers,
        body: body
      };
      // console.log(`******** ${requestType} *********`);
      // console.log(resObj);
      // console.log('----------------------------------');
      return resolve(resObj);
    });
  });
};

module.exports = expObj;

【问题讨论】:

  • 这是explicit promise construction anti-pattern。这是您应该解决的第一件事。 new Promise() 仅在您承诺基于回调的 API 时才需要,而您不是。同样,您应该从request 切换到request-promise
  • 您应该解决的下一个问题是依赖外部范围变量(在本例中为apiCardItem)来包含异步状态。您的承诺处理程序的返回值应该包含操作结果。
  • 谢谢@Tomalak。您的指点帮助解决了问题。
  • 您能否发布一个答案,总结错误的原因以及您采取了哪些措施来修复它,并参考您问题中的示例代码?

标签: node.js promise es6-promise


【解决方案1】:

了解 new Promise() 仅在承诺基于回调的 API 时才需要使用,更改为 request-promise 并在 cli.js 中返回我的承诺解决了我的问题。以这种方式正确地维护了执行流程。

对以下文件的更改如下: cli.js

const testAllCards = () => {
  return controller.testAllCards(config.get('CARD_NUMBERS'))
    .then((obj) => {
      console.log('testAllCards has been run');
    })
    .catch((e) => {
    console.log(e)
      console.log('testCards has been run with an error!');
      const _err = new ErrHandler(e, eTopicName, eSnsSubject);
      return _err.handle()
        .then(() => {
          console.log('Error has been sent with success to sns');
        })
        .catch((e) => {
          console.log('Failed to publish to sns');
          console.log(e);
        });
    });
};

测试请求到 API

'use strict';

const checkStatus = require('./../utils/status-code-checker');
const formHeaders = require('./../utils/form-req-headers');
const rqp         = require('request-promise');
const expObj = {};

//@requestType {string} - defines which headers and function name to use
//@item {object} - defines item that is being used
expObj.testRequestToApi = (requestType, lowerLimit, upperLimit, item) => {
  const reqOps = formHeaders[requestType](item);
  return rqp(reqOps)
    .then((response) => {
      if (!checkStatus.checkRangeStatusCode(response.statusCode, lowerLimit, upperLimit)) {
        console.log(JSON.stringify(response.body, null, 2));
        // Set a bad Status error object
        return {
          StatusCode: response.statusCode,
          ErrorMessage: response.body,
          FuncName: requestType,
          InternalError: false
        };
      }
      // console.log(response.headers);
      // console.log(response.body);
      const resObj = {
        headers: response.headers,
        body: response.body,
        previousItem: item
      };
      // console.log(`******** ${requestType} *********`);
      // console.log(resObj);
      // console.log('----------------------------------');
      return resObj;
    })
    .catch((e) => {
      return {
        ErrorMessage: e,
        FuncName: requestType,
        InternalError: true
      };
    });
};

module.exports = expObj;

controller.js

//Tests response from API for different cards
Controller.testCard = (cardNum) => {
  const expMonth = new Date().getMonth() + 1;
  const expYear = new Date().getFullYear() + 2;
  const cardObj = {
    cardNum: cardNum,
    expMonth: expMonth,
    expYear: expYear
  };
  let apiCardItem = '';
  return testRequestToApi('getStripeToken', 200, 299, cardObj)
    .then((cardItem) => {
      return testRequestToApi('postNewCard', 200, 299, JSON.parse(cardItem.body));
    })
    .then((apiCard) => {
      apiCardItem = apiCard.body;
      try {
        apiCardItem = JSON.parse(apiCardItem);
      } catch(e) {
        console.log('Already a JSON object -----> Moving on');
      }
      return testRequestToApi('sampleAddToCart', 200, 299);
    })
    .then(() => testRequestToApi('useFailingStripeCards', 400, 499, apiCardItem.id))
    .then(() => testRequestToApi('deleteCard', 200, 299, apiCardItem.id));
};

//Loops through the card numbers and runs the test command against them
Controller.testAllCards = (cardsArray) => {
  return Promise.all(cardsArray.map((cardNum) => {
    return Controller.testCard(cardNum);
  }));
};

module.exports = Controller;

【讨论】:

  • 好答案。我已经准备好几乎相同的代码,但是在更具哲学性质的细节上有些迷失,所以我想指出我看到的两个主要问题足以让你弄清楚。 :)
【解决方案2】:

我重写了你的“testCard”函数(controller.js)

//Tests response from API for different cards
const testCard = (cardNum) => {
  return new Promise((resolve, reject) => {
    let apiCardItem = '';
    const expDate = new Date();
    const cardObj = {
      cardNum:  cardNum,
      expMonth: expDate.getMonth() + 1,
      expYear:  expDate.getFullYear() + 2
    };
    
    testRequestToApi('getStripeToken', 200, 299, cardObj)

      .then((cardItem) => testRequestToApi('postNewCard', 200, 299, JSON.parse(cardItem.body)))

      .then((apiCard) => {
        apiCardItem = apiCard.body;
        try {
          apiCardItem = JSON.parse(apiCardItem);
        } catch(e) {
          console.log(e);
        }
        return testRequestToApi('sampleAddToCart', 200, 299);
      })

      .then(() => testRequestToApi('useFailingStripeCards', 400, 499, apiCardItem.id))

      .then(() => testRequestToApi('deleteCard', 200, 299, apiCardItem.id))

      .then(resolve)

      .catch(reject);
  });
};

还有你的“testRequestToApi”(test-request-to-api.js)

expObj.testRequestToApi = (requestType, lowerLimit, upperLimit, item) => {
  return new Promise((resolve, reject) => {
    const reqOps = formHeaders[requestType](item);

    request(reqOps, (err, response, body) => {
      let badStatus  = {};
      let badRequest = {};
      let resObj     = {};

      if (err) {
        badRequest = {
          ErrorMessage:  err,
          FuncName:      requestType,
          InternalError: true
        };

        reject(badRequest);
        return false;
      }
      
      if (!checkStatus.checkRangeStatusCode(response.statusCode, lowerLimit, upperLimit)) {
        console.log(JSON.stringify(body, null, 2));
        // Set a bad Status error object
        badStatus = {
          StatusCode:    response.statusCode,
          ErrorMessage:  body,
          FuncName:      requestType,
          InternalError: false
        };

        reject(badStatus);
        return false;
      }

      resObj = {
        headers: response.headers,
        body:    body
      };

      resolve(resObj);
    });
  });
};

我认为问题在于当您在调用 resolve/reject 之前从 Promise 返回时。

为了简洁起见,还要检查在嵌套的 Promise 中您是否可以将 resolve/reject 传递给 .then/catch。

【讨论】:

  • 还是不行。同样的问题。 'testAllCards has been run' 在其他功能上的日志之前仍然被记录
猜你喜欢
  • 1970-01-01
  • 2018-09-19
  • 2022-01-17
  • 1970-01-01
  • 2021-03-27
  • 2019-12-03
  • 2021-12-20
  • 1970-01-01
相关资源
最近更新 更多