web-dev-qa-db-ja.com

AWS Lambdaでの非同期/待機による未処理のプロミス拒否

最近AWSは、ラムダ関数用のnodejs8.10ランタイムの利用可能性を発表しました( Node.js 8.10ランタイムが利用可能 )。これはハッピーフローに最適であるように見えましたが、不幸なフローでいくつかの問題が発生しています。つまり、「UnhandledPromiseRejectionWarnings」が発生しています。

以下のコードを使用しています。その考えは、そのシナリオの不幸な流れをテストするために、存在しないオブジェクトへのキーを提供することです。私の目標は、エラーをログに記録してラムダから伝播させることです。ここでそれを処理する適切な方法がないためです(このラムダ関数内で新しいキーを取得する方法はありません)。また、呼び出し元でエラーを使用できるようにしたいと思います(たとえば、別のラムダ関数またはステップ関数)。

'use strict';

const AWS = require('aws-sdk');

exports.handler = async (event) => {
  let data;
  try {
    data = await getObject(event.key);
  } catch (err) {
    console.error('So this happened:', err);
    throw err;
  }

  return data;
}

const getObject = async (key) => {
  let params = {
    Bucket: process.env.BUCKET,
    Key: key
  };

  const s3 = new AWS.S3();

  let data;
  try {
    data = await s3.getObject(params).promise();
  } catch(err) {
    console.log('Error retrieving object');
    throw err;
  }

  console.log('Retrieved data');
  return data.Body.toString('utf8');
}

このラムダ関数を実行すると( SAM local を使用)、ラムダからエラーが返されますが、次の警告も表示されます。

2018-04-18T07:54:16.217Z    6ecc84eb-46f6-1b08-23fb-46de7c5ba6eb    (node:1) UnhandledPromiseRejectionWarning: NoSuchKey: The specified key does not exist.
    at Request.extractError (/var/task/node_modules/aws-sdk/lib/services/s3.js:577:35)
    at Request.callListeners (/var/task/node_modules/aws-sdk/lib/sequential_executor.js:105:20)
    at Request.emit (/var/task/node_modules/aws-sdk/lib/sequential_executor.js:77:10)
    at Request.emit (/var/task/node_modules/aws-sdk/lib/request.js:683:14)
    at Request.transition (/var/task/node_modules/aws-sdk/lib/request.js:22:10)
    at AcceptorStateMachine.runTo (/var/task/node_modules/aws-sdk/lib/state_machine.js:14:12)
    at /var/task/node_modules/aws-sdk/lib/state_machine.js:26:10
    at Request.<anonymous> (/var/task/node_modules/aws-sdk/lib/request.js:38:9)
    at Request.<anonymous> (/var/task/node_modules/aws-sdk/lib/request.js:685:12)
    at Request.callListeners (/var/task/node_modules/aws-sdk/lib/sequential_executor.js:115:18)
2018-04-18T07:54:16.218Z    6ecc84eb-46f6-1b08-23fb-46de7c5ba6eb    (node:1) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
2018-04-18T07:54:16.218Z    6ecc84eb-46f6-1b08-23fb-46de7c5ba6eb    (node:1) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

ラムダ関数からエラーを伝播しながらこれを処理する方法がわかりません(これは lambda function errors(node.js) に従って有効なシナリオになるはずです)。

以下のような(私にとって)同様のシナリオを実行しようとしましたが(エラーを特定して正確に理解するため)、どういうわけかここでは警告が表示されず、意図したとおりに機能しています(エラーはラムダから返されます)関数)。

'use strict';

const AWS = require('aws-sdk');

exports.handler = async (event) => {
  let data;
  try {
    data = await directBoom();
  } catch (err) {
    console.error('So this happened:', err);
    throw err;
  }

  return data;
}

const directBoom = async () => {
  let data;
  try {
    data = await Promise.reject(new Error('boom!')); 
  } catch(err) {
    throw err;
  }

  return data;
}

ここで何が欠けているのですか、2つの例の動作が異なるのはなぜですか?ラムダ関数の外にエラーを伝播できるようにしながら、最初の例の警告を取り除くにはどうすればよいですか?任意の助けいただければ幸いです。

9
CodeVision

Promise/async関数内でスローまたはリジェクトし、それをキャッチで処理しない場合は常に、Nodeがその警告を返します。

AWSの example は、catchブロックでエラーをスローせず、それを返します。

let AWS = require('aws-sdk');
let lambda = new AWS.Lambda();
let data;

exports.handler = async (event) => {
    try {
        data = await lambda.getAccountSettings().promise();
    }
    catch (err) {
        console.log(err);
        return err;
    }
    return data;
};

多数の非同期関数を持つ大規模なアプリケーションでは、単一の未処理のpromiseがノードプロセスを終了することは好ましくありません。これは、プロセスを終了するエラーをスローすることが望ましい動作である単純なLambda関数には適用されない場合があります。ただし、Nodeで警告したくない場合は、代わりにエラーを返します。

5
Ryan Flaherty

AWSが問題を修正したようです。 Node 8.10ランタイムを使用して次の関数でテストすると、未処理の拒否が表示されなくなります。

exports.handler = async (event) => { 
  throw new Error("broken")
};
2
Martin Donath

実行が失敗したことをLambda関数に通知するコンテキストオブジェクトからfailメソッドを使用して管理しました。

'use strict'

exports.handler = async function (event, context) {
  try {
    throw new Error('Something went wrong')
  } catch (err) {
    context.fail(err)
  }
}
0
Kevin Rambaud