Как я могу избежать двойного вызова блока catch моего Q обещания в API в стиле обратного вызова NodeJS при запуске модульных тестов мокко?

Мы используем библиотеку обещаний Q в нашем API узла, но разрешаем вызов функций через обратные вызовы.

Например:

function foo(cb) {
 Q.fcall(function () {
    return true;
 }).then(function (result) {
    return cb(null, result);
 }).catch(function (err) {
    return cb(err, null);
 });
}

Когда я запускаю свои модульные тесты мокко, если есть исключение в обратном вызове, это приводит к тому, что обратный вызов вызывается дважды.

Например:

var called = 0;
foo(function (err, res) {
  called++;
  console.log('err: ' + err);
  console.log('res: ' + res);
  console.log('called: ' + called);

  throw Error(throw Error from foo!');
});

Это дает следующий результат:

err: null res: true называется: 1 err: Error: throw Error from foo! res: null вызвано: 2

Один из подходов, который мы нашли, заключался в следующем:

function foo2(cb) {
 var _result, _err;
 Q.fcall(function () {
     return true;
  }).then(function (result) {
     _result = result;
  }).catch(function (err) {
     _err = err;
  }).finally(function () {
     _.defer(cb, _err, _result);
  });
}

Идея заключалась в том, чтобы иметь одно место, где будет вызываться обратный вызов, и попытаться предотвратить ошибки разработчика, принудительно применяя функцию defer для очистки стека вызовов.

При таком подходе наш обратный вызов вызывается один раз, и любые ошибки (или, в нашем случае, утверждения) обрабатываются непосредственно вызывающим.

Есть ли лучший метод, который мы можем использовать? Мне кажется, что это довольно распространенный сценарий, поэтому я полагаю, что существует более чистое решение ...


person Patrick Bounaix    schedule 31.08.2015    source источник
comment
err: null res: true called: 1 err: Error: throw Error from foo! res: null called: 2 ‹- поймал один раз. Как вы думаете, почему он называется дважды? Обратный вызов БЫЛ вызывался дважды, но это то, что вы хотели, не так ли?   -  person Amit    schedule 31.08.2015
comment
Нет, это именно то, чего я хочу избежать. Обратный вызов следует вызывать один раз из foo. В первом случае он вызывается один раз при успешном выполнении, а затем еще раз при обнаружении ошибки, потому что блок перехвата обещания все еще имеет управление.   -  person Patrick Bounaix    schedule 31.08.2015
comment
Взгляните на Когда .then (успех, неудача) считается антипаттерном для обещаний? (затем примите ответ Амита)   -  person Bergi    schedule 01.09.2015


Ответы (1)


Измените функцию foo, чтобы обрабатывать как выполнение, так и отклонение в одном вызове then с использованием двух отдельных обработчиков:

function foo(cb) {
 Q.fcall(function () {
    return true;
 }).then(function (result) {
    return cb(null, result);
 }, function (err) {
    return cb(err, null);
 });
}
person Amit    schedule 31.08.2015