Exécution imprévisible du code asynchrone dans Lambda node.js
Je ne comprends pas comment utiliser le code asynchrone dans Lambda. Les résultats sont déconcertants, construisons 2 fonctions:
const sendToFirehoseAsync = async (param) => {
console.log(param);
const promise = new Promise(function(resolve, reject) {
var params = {
DeliveryStreamName: 'TestStream',
Records: [{ Data: 'test data' }]
};
console.log('params', params);
firehose.putRecordBatch(params, function (err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log('Firehose Successful', data); // successful response
});
});
return promise;
}
et
const sendToFirehoseSync = (param) => {
console.log(param);
var params = {
DeliveryStreamName: 'TestStream',
Records: [{ Data: 'test data' }]
};
console.log('params', params);
firehose.putRecordBatch(params, function (err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log('Firehose Successful', data); // successful response
});
}
Maintenant, lançons-les et voyons ce qui se passe!
Exécutez la fonction Async - Cela fonctionne très bien.
exports.handler = async (event) => {
let res = await sendToFirehoseAsync('test1');
return res;
}
2020-11-05T20:19:16.146+13:00 START RequestId: e4c505ea-1717-4998-ad0d-a42434f0a0c1 Version: $LATEST
2020-11-05T20:19:16.148+13:00 2020-11-05T07:19:16.147Z e4c505ea-1717-4998-ad0d-a42434f0a0c1 INFO test1
2020-11-05T20:19:16.149+13:00 2020-11-05T07:19:16.149Z e4c505ea-1717-4998-ad0d-a42434f0a0c1 INFO params { DeliveryStreamName: 'TestStream', Records: [ { Data: 'test data' } ] }
2020-11-05T07:19:16.245Z e4c505ea-1717-4998-ad0d-a42434f0a0c1 INFO Firehose Successful {
FailedPutCount: 0,
Encrypted: false,
....
Cependant, si j'appelle la fonction deux fois avec await (voir ci-dessous), j'obtiens exactement la même réponse (c'est-à-dire que je ne vois pas le console.log pour le test 2, etc. C'est comme si le deuxième appel n'arrive jamais? J'ai supposé awaitqu'il arrêterait l'exécution jusqu'à ce que la première fonction soit résolue, puis continuerait.
let res = await sendToFirehoseAsync('test1');
res = await sendToFirehoseAsync('test2');
return res;
Maintenant, courons quelques autres dos à dos:
console.log('async call 1');
await sendToFirehoseAsync('test1');
console.log('async call 2');
await sendToFirehoseAsync('test2');
console.log('sync call 1');
let resp1 = await sendToFirehoseSync('1');
console.log('sync call 2');
let resp2 = await sendToFirehoseSync('2');
console.log('after sync calls');
2020-11-05T20:35:28.465+13:00 2020-11-05T07:35:28.464Z 5a9e551f-ecc6-4f18-8af4-a11b1b29d835 INFO async call 1
2020-11-05T20:35:28.465+13:00 2020-11-05T07:35:28.465Z 5a9e551f-ecc6-4f18-8af4-a11b1b29d835 INFO test1
2020-11-05T20:35:28.467+13:00 2020-11-05T07:35:28.467Z 5a9e551f-ecc6-4f18-8af4-a11b1b29d835 INFO params { DeliveryStreamName: 'TestStream', Records: [ { Data: 'test data' } ] }
2020-11-05T20:35:28.577+13:00 2020-11-05T07:35:28.577Z 5a9e551f-ecc6-4f18-8af4-a11b1b29d835 INFO Firehose Successful { FailedPutCount: 0, Encrypted: false, RequestResponses: [ { RecordId: '4v+6H3T3koBggYYvdu/U6fg4h0C8m4taPVYznfYT4fIAWmm9XKu4/9F9jEgjdZFE02IsNgYs0/ORGzz1l2udEzCJUN1dRR1YCHSi/jiLI/DHGpTkoyN89VUG0jGzNlAERgUNCIwxXlCYww/l2HSGjK8++f+qmRj7sTCY/J4/QlV2sqhcXSlJjKhkK+A+Ib7w2+WwdZ5gliF64fSP9qkQSpeSutOh68o6' } ] }
2020-11-05T20:35:28.580+13:00 END RequestId: 5a9e551f-ecc6-4f18-8af4-a11b1b29d835
Encore une fois, un seul résultat revient. Le reste est perdu?!
Et j'obtiens le même résultat avec 2 appels asynchrones, suivis de 2 appels de synchronisation et d'un autre pour faire bonne mesure.
console.log('async call 1');
await sendToFirehoseAsync('test1');
console.log('async call 2');
await sendToFirehoseAsync('test2');
console.log('sync call 1');
let resp1 = await sendToFirehoseSync('1');
console.log('sync call 2');
let resp2 = await sendToFirehoseSync('2');
console.log('after sync calls');
const promise = new Promise(function(resolve, reject) {
var params = {
DeliveryStreamName: 'TestStream',
Records: [{ Data: 'test data 2' }]
};
console.log('params', params);
firehose.putRecordBatch(params, function (err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log('Firehose Successful', data); // successful response
});
});
return promise;
Cependant ... Si je réexécute le dernier exemple mais que je commente les 2 appels asynchrones, j'obtiens quelque chose de différent ...
console.log('sync call 1');
let resp1 = await sendToFirehoseSync('1');
console.log('sync call 2');
let resp2 = await sendToFirehoseSync('2');
console.log('after sync calls');
const promise = new Promise(function(resolve, reject) {
var params = {
DeliveryStreamName: 'TestStream',
Records: [{ Data: 'test data 2' }]
};
console.log('params', params);
firehose.putRecordBatch(params, function (err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log('Firehose Successful', data); // successful response
});
});
return promise;
2020-11-05T20:42:08.713+13:00
2020-11-05T07:42:08.713Z 333feae9-f306-409c-89c8-1707e0547ba3 INFO sync call 1
2020-11-05T07:42:08.713Z 333feae9-f306-409c-89c8-1707e0547ba3 INFO sync call 1
2020-11-05T20:42:08.713+13:00 2020-11-05T07:42:08.713Z 333feae9-f306-409c-89c8-1707e0547ba3 INFO 1
2020-11-05T20:42:08.715+13:00 2020-11-05T07:42:08.715Z 333feae9-f306-409c-89c8-1707e0547ba3 INFO params { DeliveryStreamName: 'TestStream', Records: [ { Data: 'test data' } ] }
2020-11-05T20:42:08.760+13:00 2020-11-05T07:42:08.760Z 333feae9-f306-409c-89c8-1707e0547ba3 INFO after sync calls
2020-11-05T20:42:08.760+13:00 2020-11-05T07:42:08.760Z 333feae9-f306-409c-89c8-1707e0547ba3 INFO params { DeliveryStreamName: 'TestStream', Records: [ { Data: 'test data 2' } ] }
2020-11-05T20:42:08.808+13:00 2020-11-05T07:42:08.807Z 333feae9-f306-409c-89c8-1707e0547ba3 INFO Firehose Successful { FailedPutCount: 0, Encrypted: false, RequestResponses: [ { RecordId: 'iWeCDK6kukfkLfh/1mg791g3sIVpDC1hNNokJuTGFJJaLBNd1TvvCiWHV4z2iiWS3hOvu9OmKVnUofCPbr5uewKPAQBdiCJp9iVIzTakcL5bb4CkyOZKxzLX4NOxTP94Z0j64KgssWo10z7jEhDoevF8NTMZR+tUlhHmYtEGcQq2YViwwXhpYX8MP4yvS5xSRo+sjJXEcyoty+Pvt1UFWGelEKIygtnO' } ] }
2020-11-05T20:42:08.865+13:00 2020-11-05T07:42:08.865Z 333feae9-f306-409c-89c8-1707e0547ba3 INFO Firehose Successful { FailedPutCount: 0, Encrypted: false, RequestResponses: [ { RecordId: 's5loZTT8d4J0fhSjnJli0LzOzljnvgvC99AvdSeqkj/j9xp5RnjstL5UxQXm5t+uyEbSSe21XZxwaUU/D7XVsCzpJ6F5nlnzsOZBLd6vyaF3bc2lSUo2DM2u9dGetJPMahC1b0rO+GXod91sC9XumS8QWIVePcww2DH0IM46RuoLEVVR3/kgcnvhIm/UU67JuvZkFTCAP/jss0VwVUY2vmzfdvw4mJT4' } ] }
2020-11-05T20:42:08.867+13:00 END RequestId: 333feae9-f306-409c-89c8-1707e0547ba3
Le seul modèle que je peux voir est le retour. La fonction async a un retour en elle. Cela provoque-t-il peut-être le retour de tout le lambda, pas seulement de la fonction? J'espère que cette longue expérience (malheureusement) sera utile et que quelqu'un pourra vous éclairer sur la façon dont cela fonctionne. À votre santé.
** Ajout de la résolution et du rejet **
console.log('before');
await sendToFirehosePromise('thing');
console.log('after');
....
async function sendToFirehosePromise(record) {
var params = {
DeliveryStreamName: 'TestStream',
Records: [{ Data: 'test data' }]
};
const promise = new Promise((resolve, reject) => {
firehose.putRecordBatch(params, (err, data) => {
if (err) return reject(err);
return resolve(data);
});
});
return promise;
}
Réponses
Réduisons autant que possible les appels asynchrones purs d'un lambda:
function f(p) {
console.log(p);
return new Promise(res => res('result ' + p));
}
exports.handler = async () => {
let res = await f(1);
console.log(res);
res = await f(2);
console.log(res);
res = await f(3);
console.log(res);
}
Impressions:
2020-11-05T10:40:25.298Z bd564d91-480f-4dcd-8134-1481fa59a946 INFO 1
2020-11-05T10:40:25.298Z bd564d91-480f-4dcd-8134-1481fa59a946 INFO result 1
2020-11-05T10:40:25.298Z bd564d91-480f-4dcd-8134-1481fa59a946 INFO 2
2020-11-05T10:40:25.298Z bd564d91-480f-4dcd-8134-1481fa59a946 INFO result 2
2020-11-05T10:40:25.298Z bd564d91-480f-4dcd-8134-1481fa59a946 INFO 3
2020-11-05T10:40:25.299Z bd564d91-480f-4dcd-8134-1481fa59a946 INFO result 3
Maintenant, comparez le vôtre. Tout d' abord cette signature est faux (ou tout au moins inutiles): const sendToFirehoseAsync = async (param). Async n'est requis que si vous attendez quelque chose. Si vous n'attendez pas - et vous ne l'êtes pas - vous n'avez pas besoin de le marquer comme asynchrone. Une fonction asynchrone peut attendre tout ce qui renvoie une promesse. Si votre fonction renvoie une promesse et n'attend rien, ne la marquez pas comme asynchrone.
Passons maintenant à la partie où vous mélangez des promesses et asynchronisez inutilement.
async function sendToFirehosePromise(record) {
var params = {
DeliveryStreamName: 'TestStream',
Records: [{ Data: 'test data' }]
};
const promise = new Promise((resolve, reject) => {
firehose.putRecordBatch(params, (err, data) => {
if (err) return reject(err);
return resolve(data);
});
});
return promise;
}
Si vous essayez d'utiliser async, utilisez async. Tous les appels du kit AWS SDK renvoient un type de AWS.Request. Ce type contient une promise()méthode. Vous pouvez attendre cette promesse plutôt que d'utiliser la notation de promesse réelle.
async function sendToFirehosePromise(record) {
var params = {
DeliveryStreamName: 'TestStream',
Records: [{ Data: 'test data' }]
};
return await firehose.putRecordBatch(params).promise();
}
C'est une bonne utilisation de async / await avec le SDK plutôt que de le transformer en promesses personnalisées. Maintenant, dans votre gestionnaire, vous pouvez attendre cette fonction autant de fois que vous le souhaitez et cela fonctionnera toujours.