Поэтому я разрабатывал несколько кодов, используя AWS Lambda с NodeJS 6.10. Из-за отсутствия у меня знаний в области интеграционного тестирования (не волнуйтесь, модульные тесты выполнены) я не тестировал свой код. Конечно, я пропустил ошибку, из-за которой две бессонные ночи. Он продолжает работать даже после того, как я вставил это
return workerCallback(err);
Я думал, что это помешает функции запускать другие коды после предложения if, потому что я его вернул. Во всяком случае, я смог исправить свою проблему, добавив возврат сразу после асинхронной функции
SQSService.deleteMessage
называется. Остальные коды не запускались, а лямбда-функция выполнялась и завершалась, как ожидалось.
Вот код, который работает, как ожидалось.
function myFoo(currentRequest,event, workCallback){
var current_ts = moment();
var req_ts = moment(currentRequest.request_timestamp);
if (req_ts.diff(current_ts, 'minutes') > 5) {
SQSService.deleteMessage(event.ReceiptHandle, function(err, data){
if (err) {
return workerCallback(err);
} else {
return workerCallback(null, "Request stale! Deleting from queue...");
}
}); //end of SQS Service
return; //This line... this line!
}
/* Codes below will execute because the code above is asynchronous
but it should not as the asynchronous code above is a terminator function
from a business logic point of view
*/
//more codes that will run should currentRequest.request_timestamp is 5 minutes past
}
Может кто-нибудь посоветовать мне, как протестировать этот код или создать тест, который, по крайней мере, помешал бы мне повторить ту же ошибку снова? Я бы хотел избежать повторения этих ошибок путем тестирования. Спасибо!
ах да ... функция получает сообщения с project_ref_no или без него. Итак, если project_ref_no не определено, удалите сообщение и готово. Полагаю, я мог бы добавить еще для предложения if. Как бы вы это написали?
(Я перехожу к ответу, чтобы поток комментариев не заполнялся - и чтобы я мог печатать больше).
Ключ в том, чтобы получить правильное представление об асинхронности вашего кода. myFoo
кажется асинхронным, поэтому вам нужно решить, все ли ошибки или режимы отказа должны обрабатываться как ошибки, передаваемые его обработчику обратного вызова, или некоторые типы ошибок должны возвращать синхронные ошибки вызывающему myFoo
самому. Мой общий подход заключается в том, что если какие-либо ошибки проходят через обработчик обратного вызова, все они отправляются туда - с незначительным исключением определенных типов ошибок неправильного кодирования (например, передача вещей неправильного типа или передача null для аргументов в котором всегда должны быть переменные), для которых я мог бы использовать throw Error()
. Но если такая ошибка (project_ref_no == null
) является той ошибкой, которую вы должны аккуратно обрабатывать, то я, вероятно, передаю ее обработчику ошибок. Общая идея заключается в том, что когда вы вызываете myFoo, и он возвращается, все, что вы знаете, это то, что некоторая работа будет выполнена в какой-то момент, но вы не знаете, что произойдет (и не получите результата. в ответе) - ответ вернется позже при вызове обработчика обратного вызова).
Но, что более важно, важно понимать, какой код запускается немедленно и какой код находится в обработчике обратного вызова. Вы запутались, потому что мысленно представляете, что обработчик внутреннего сгенерированного обратного вызова (переданный в SQSService.deleteMessage
) запускался, когда вы вызывали myFoo.
Что касается стратегии тестирования, я не думаю, что есть серебряная пуля в проблеме ошибочного принятия асинхронных вызовов (с обработчиками обратного вызова) с кодом, который выполняется синхронно. Вы можете разбрасывать утверждения или throw Error()
повсюду (где, как вы думаете, код никогда не должен попадать), но это сделало бы ваш код смешным.
Typescript немного помогает в этом, потому что вы можете определить тип, возвращаемый функцией, и ваша IDE должна выдавать вам предупреждение, если у вас есть пути кода, которые не возвращают что-то этого типа (что-то больше / все? Типизированные языки дают вам ) - и это немного поможет, но не все случаи (например, функции, возвращающие void).
Если вы новичок в асинхронных моделях javascript и / или javascript, вы можете проверить следующую ссылку:
Я отвечу, поскольку пытаюсь понять ваш ответ. Надеюсь, это не означает, что я отказался от вопроса. Спасибо!
Я изменяю код, чтобы сделать его более ориентированным на бизнес-логику. Я просто не знал, что после вызова async коды после этого все равно будут работать. Я думаю, что это фундаментальный способ мышления о том, как кодировать асинхронные функции. Я действительно предполагал, что повторный вызов workerCallback - это то, что закроет функцию. Коды кстати обновил.
Чтобы было ясно, async - это ключевое слово, которое имеет определенное значение в JavaScript. Метод является асинхронным, если он выполняет асинхронный вызов (например, асинхронный вызов ввода-вывода или какая-либо другая функция, которая принимает обработчик обратного вызова и обещает что-то с ним сделать позже). Ключевое слово async перед определением функции позволяет вам кодировать с помощью асинхронных методов, которые действительно возвращают Promise <XXX>, как если бы это синхронный метод, возвращающий что-то типа XXX. По сути, он «блокирует» вызывающий код до тех пор, пока обещание вызываемого кода не будет выполнено. Вы не используете асинхронный код в своем коде.
Да нет. Это должно прояснить ситуацию. Что касается обещаний, я не удивлен, что об этом здесь упоминается. Я пытался сначала понять, как работают асинхронные функции, прежде чем погрузиться в обещания. Хотя спасибо за вклад
Асинхронное программирование в старом стиле передается в обработчики обратных вызовов, которые, как ожидается, будут обрабатывать (более поздний) результат. Обещания возвращают дескриптор (отложенного) результата вызывающему, поэтому вызывающий может делать с ним все, что хочет (ждать, собирать несколько и ждать их вместе, передавать их какому-то другому коду и т. д.), И async / await основывается на обещаниях и позволяет вызывающему коду эффективно «блокировать» результат и делать вид, что он синхронный (в то же время позволяя другим «потокам» работать).
Спасибо. Я надеюсь, что некоторые другие разработчики смогут получить представление об этом посте.
помните - вызов SQSService.deleteMessage включает обработчик обратного вызова - и именно в этом обработчике обратного вызова существуют ваши два оператора возврата. Они завершают обработчик обратного вызова всякий раз, когда метод SQSService решает вызвать его. Но они не выполняются в myFoo, поэтому после вызова SQSService.deleteMessage код просто продолжится. Что касается тестирования, я полагаю, вы могли бы поставить утверждение или выдать ошибку после кода, который, по вашему мнению, никогда не должен пройти. Но это не совсем системное решение. Более важный вопрос - зачем вообще нужен код?