web-dev-qa-db-fra.com

Rejet de promesse non gérée sur AWS lambda avec asynchrone/wait

Récemment, AWS a annoncé la disponibilité du runtime nodejs8.10 pour ses fonctions lambda ( Node.js 8.10 runtime available ). Même si cela semblait génial pour le flux heureux, je rencontre quelques problèmes avec le flux malheureux, c’est-à-dire que je reçois le message 'UnhandledPromiseRejectionWarnings'.

J'utilise le code ci-dessous. L'idée est que je fournisse une clé à un objet non existant pour tester le flux malheureux de ce scénario. Mon but est que l'erreur soit enregistrée et propagée hors du lambda puisque je n'ai aucun moyen sensé de la gérer ici (je n'ai aucun moyen de récupérer une nouvelle clé dans cette fonction lambda). Je voudrais aussi pouvoir utiliser l'erreur dans l'appelant (par exemple une autre fonction lambda ou des fonctions d'étape).

'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');
}

Si j'exécute cette fonction lambda (avec SAM local ), l'erreur est renvoyée hors du lambda comme je le souhaite, mais les avertissements suivants sont également générés:

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.

Je ne suis pas sûr de savoir comment gérer cela tout en propageant l'erreur en dehors de la fonction lambda (ce qui devrait être un scénario valide selon erreurs de la fonction lambda (node.js) )

J'ai également essayé d'exécuter un scénario similaire (pour moi) similaire à celui ci-dessous (pour essayer d'identifier et de comprendre l'erreur), mais d'une manière ou d'une autre, je ne reçois pas l'avertissement et il fonctionne comme prévu (l'erreur est renvoyée par le lambda une fonction).

'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;
}

Qu'est-ce qui me manque ici et pourquoi les deux exemples se comportent-ils différemment? Comment me débarrasser de l'avertissement dans le premier exemple tout en étant capable de propager l'erreur à partir de la fonction lambda? Toute aide serait appréciée.

9
CodeVision

Chaque fois que vous jetez ou rejetez une fonction promesse/asynchrone et que vous ne la manipulez pas avec un catch, Node renvoie cet avertissement. 

Le exemple sur AWS ne renvoie pas l'erreur dans le bloc catch, il la renvoie: 

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;
};

Dans une application volumineuse comportant de nombreuses fonctions asynchrones, le processus de nœud ne comporterait qu'une seule promesse non gérée. Cela peut ne pas s’appliquer à une simple fonction Lambda dans laquelle le comportement souhaité est de générer une erreur qui termine le processus. Mais si vous ne souhaitez pas que Node vous avertisse, renvoyez l'erreur à la place. 

5
Ryan Flaherty

Il semble qu'AWS ait résolu le problème. En testant avec la fonction suivante utilisant le runtime Node 8.10, je ne vois plus de rejets non gérés:

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

J'ai géré cela en utilisant la méthode fail à partir de l'objet context qui informe la fonction Lambda que l'exécution a échoué.

'use strict'

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