tl;dr То, что вы наблюдаете, возможно, является ошибкой в ​​коде обработчика среды выполнения Lambda.

Небольшой контекст:

Lambda до версии 8 предлагала только разрешение на основе обратного вызова, в котором вы должны были передать ошибку или значение предоставленному обратному вызову, переданному вашей функции-обработчику. С Node 8 они начали поддерживать асинхронные функции, которые вы используете. Вы можете вспомнить, что асинхронные функции всегда возвращают обещание, даже если ваш код возвращает примитивное значение. Код платформы Lambda обнаруживает, что вы синхронно вернули выполненное обещание, и немедленно завершает вызов. Конечно, если бы вы await выключили свой таймер, он дождался бы завершения, прежде чем продолжить.

Lambda также имеет атрибут объекта context с именем callbackWaitsForEmptyEventLoop, который по умолчанию имеет значение true. Идея этого состоит в том, чтобы содержать асинхронное поведение вызова внутри себя. Якобы это включает в себя таймеры без unref, сетевые подключения, операции с файловой системой и т. д. Вы можете найти обсуждение этой функции, в основном людей, которые удивлены тем, что она поддерживает их вызов из-за длительного подключения к базе данных.

Создаваемый вами объект таймера основан на времени часов, а не на прошедшем времени процессора. Конкретный экземпляр вашей функции запускается один раз, а затем переходит в спящее состояние до тех пор, пока он снова не понадобится. Но поскольку реальное время истекло, когда он просыпается, код таймера Node видит, что срок истек, и немедленно запускает таймер.

Когда callbackWaitsForEmptyEventLoop равно true, ваш таймер должен завершиться до завершения вызова. То, что это не так, является ошибкой в ​​Lambda.

Забавный факт / плагин: уровень выполнения NodeSource N|Solid на самом деле ведет себя правильно в этом сценарии и будет ждать завершения вашего таймера, даже если он возвращает уже выполненное обещание.