Unvorhersehbare Ausführung von asynchronem Code in Lambda node.js

Nov 05 2020

Ich kann nicht verstehen, wie man asynchronen Code in Lambda verwendet. Die Ergebnisse sind verblüffend. Lassen Sie uns zwei Funktionen erstellen:

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

und

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

Lassen Sie uns sie jetzt hochfahren und sehen, was passiert!

Async-Funktion ausführen - Funktioniert einwandfrei.

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,
        ....

Wenn ich die Funktion jedoch zweimal mit Warten aufrufe (siehe unten), erhalte ich genau die gleiche Antwort (dh ich sehe das console.log für Test 2 usw. nicht. Es ist, als ob der zweite Aufruf nie stattfindet? Was ist los? Ich nahm an, awaitdass die Ausführung angehalten werden würde, bis die erste Funktion aufgelöst wurde, und dann fortfahren würde. Ganz klar nicht.

let res = await sendToFirehoseAsync('test1');
res = await sendToFirehoseAsync('test2');
return res;

Lassen Sie uns jetzt noch ein paar Schritte hintereinander laufen:

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

Wieder kommt nur 1 Ergebnis zurück. Der Rest ist verloren?!

Und ich erhalte das gleiche Ergebnis mit 2 asynchronen Aufrufen, gefolgt von 2 Synchronisierungsaufrufen und einem weiteren.

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;  

Allerdings ... Wenn ich das letzte Beispiel erneut ausführe, aber die 2 asynchronen Aufrufe auskommentiere, erhalte ich etwas anderes ...

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

Das einzige Muster, das ich sehen kann, ist die Rückkehr. Die asynchrone Funktion enthält eine Rückgabe. Führt das möglicherweise dazu, dass das gesamte Lambda zurückkehrt, nicht nur die Funktion? Ich hoffe, dass dieses (leider) lange Experiment hilfreich ist und dass jemand etwas Licht ins Dunkel bringen kann, wie das funktioniert. Prost.

** Hinzufügen von Resolve & Reject **

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

Antworten

1 404 Nov 05 2020 at 10:51

Reduzieren wir es so weit wie möglich auf reine asynchrone Anrufe von einem 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);
}

Drucke:

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

Vergleichen Sie es jetzt mit Ihnen. Erstens ist diese Signatur falsch (oder zumindest unnötig) : const sendToFirehoseAsync = async (param). Async ist nur erforderlich, wenn Sie auf etwas warten. Wenn Sie nicht warten - und nicht -, müssen Sie es nicht als asynchron markieren. Eine asynchrone Funktion kann auf alles warten, was ein Versprechen zurückgibt. Wenn Ihre Funktion ein Versprechen zurückgibt und nichts erwartet, markieren Sie es nicht als asynchron.

Nun zu dem Teil, in dem Sie Versprechen und Asynchronität unnötig mischen.

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

Wenn Sie versuchen, Async zu verwenden, verwenden Sie Async. Alle AWS SDK-Aufrufe geben einen Typ von zurück AWS.Request. Dieser Typ enthält eine promise()Methode. Sie können dieses Versprechen abwarten, anstatt die tatsächliche Versprechensnotation zu verwenden.

async function sendToFirehosePromise(record) {
    var params = {
        DeliveryStreamName: 'TestStream', 
        Records: [{ Data: 'test data' }]
    };
    return await firehose.putRecordBatch(params).promise();
}

Dies ist eine ordnungsgemäße Verwendung von Async / Warten mit dem SDK, anstatt es in benutzerdefinierte Versprechen umzuwandeln. Jetzt können Sie in Ihrem Handler diese Funktion so oft abwarten, wie Sie möchten, und sie wird immer funktionieren.