Как работает обещание в лямбда-функциях Node.js и AWS - PullRequest
1 голос
/ 29 сентября 2019

Я очень новичок в Javascript и Node.js в целом.Поэтому я пытаюсь разобраться с концепцией асинхронного программирования в Node.js, а также с тем, как использовать то же самое в AWS-лямбде.

Я натолкнулся на этот пост в блогах Amazon, в котором объясняется поддержкаasync / await в лямбда-функциях Node.js: https://aws.amazon.com/blogs/compute/node-js-8-10-runtime-now-available-in-aws-lambda/

Насколько я знал, концепция функции, генерирующей обещание, заключается в следующем фрагменте кода:

const func1 = () => {
    console.log("Starting with execution of func1...")
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("First promise ended after 3 secs")
        }, 3000)
    })
}

здесь функция явно генерирует Promise и возвращает то же самое.

Но в приведенном выше сообщении в блоге AWS я вижу определение функции следующим образом:

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

exports.handler = async (event) => {
    return await lambda.getAccountSettings().promise() ;
};

Теперь я проверил AWS SDKдля документации Node.js (https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Lambda.html#getAccountSettings-property) и я вижу, что функция getAccountSettings принимает обратный вызов в качестве третьего параметра.

Я запутался в синтаксисе генерации обещаний .promise(). Как работает функцияубедитесь, что при использовании этого синтаксиса он вернет объект обещания, потому что в документации нет упоминания о том, что если я использую .promise(), он вернет обещание.Я предполагаю, что здесь может быть эмпирическое правило в этом аспекте.

Также вместо return await lambda.getAccountSettings().promise(), если я просто напишу return await lambda.getAccountSettings(), что это изменит.

Есть ли какая-либо документация поэто то, что я могу сослаться?

Прошу вас, пожалуйста, пролить немного света на этот новый способ вернуть объект обещания.

Спасибо за любую помощь заранее.

1 Ответ

1 голос
/ 30 сентября 2019

Если вы хотите понять, почему метод .promise() стал доступен, и почему обещание не было просто возвращено просто так, обратите внимание на то, как такой API развивается с течением времени, и необходимо поддерживать обратную совместимость.

Давайте построим что-то похожее, но значительно упрощенное.Давайте сделаем функцию, которая обеспечивает 1 / x для данного числа, но объект ошибки, когда x = 0.Это будет сделано асинхронно.

Кроме того, мы хотим, чтобы функция возвращала синхронно объект, который позволяет регистрировать прослушиватель для случая возникновения ошибки, другой - для случая успеха, иеще один, когда происходит одно из двух.Это очень упрощенная идея из того, что возвращает AWS: там вы получаете очень богатый Request объект.

Так что представьте, что мы находимся в 2012 году, и обещания еще не широко доступны / не используются вJavaScript.Поэтому мы предоставляем следующий API для нашей асинхронной функции 1 / x:

// Implementation in the first version of the API (without promises):
//    * returns an object with which you can register a listener
//    * accepts an optional callback
function getAsyncInverse(num, callback) {
    var onSuccess = [], // callbacks that are called on success
        onError = [], // callbacks that are called on failure
        onComplete = [];  // callbacks that are called in both situations
    if (callback) onComplete.push(callback);
    
    function complete(err=null) {
        var result = null;
        if (num === 0) err = new Error("Division by Zero");
        else result = 1/num;
        // Communicate the result/error to the appropriate listeners:
        if (err) for (var i = 0; i < onError.length; i++) onError[i](err);
        else for (var i = 0; i < onSuccess.length; i++) onSuccess[i](result);
        for (var i = 0; i < onComplete.length; i++) onComplete[i](err, result);
    }

    var timeoutId = setTimeout(complete, 100);

    var request = {
        on: function (type, callback) {
            if (type === "success") onSuccess.push(callback);
            else if (type === "error") onError.push(callback);
            else if (type === "complete") onComplete.push(callback);
            return request;
        },
        abort: function () {
            clearTimeout(timeoutId);
            complete(new Error("aborted"));
            return request;
        }
    }
    
    return request;
}

// How version 1 is used, by registering a listener via the returned object
var num = 2;
var request = getAsyncInverse(num); // let's not pass a callback here
request.on("success", function (result) { // ... but use the request object
    console.log("The result is:", result);    
}).on("error", function (err) {
    console.log("There was an error:", err);
});

Но тогда обещания становятся более популярными, и пользователи вашего API стремятся к API обещаний.Вы хотите обеспечить обратную совместимость и поэтому решили просто расширить возвращаемый объект запроса одним дополнительным свойством, методом: promise()

Вот как будет изменена вышеупомянутая реализация, чтобы это произошло:

// Implementation in the second version of the API (with promise), but backwards-compatible
//    * returns an object with which you can register a listener, or get the promise object
//    * accepts an optional callback
function getAsyncInverse(num, callback) {
    let onSuccess = [], // callbacks that are called on success
        onError = [], // callbacks that are called on failure
        onComplete = [];  // callbacks that are called in both situations
    if (callback) onComplete.push(callback);
    
    let request;
    // New: create a promise, and put the logic inside the promise-constructor callback
    let promise = new Promise(function (resolve, reject) {
        function complete(err=null) {
            let result = null;
            if (num === 0) err = new Error("Division by Zero");
            else result = 1/num;
            // Communicate the result/error to the appropriate listeners:
            if (err) for (let callback of onError) callback(err);
            else for (let callback of onSuccess) callback(result);
            for (let callback of onComplete) callback(err, result);
            // New: also call resolve/reject
            if (err) reject(err);
            else resolve(result);
        }

        let timeoutId = setTimeout(complete, 100);
        
        request = {
            on: function (type, callback) {
                if (type === "success") onSuccess.push(callback);
                else if (type === "error") onError.push(callback);
                else if (type === "complete") onComplete.push(callback);
                return request;
            },
            abort: function () {
                clearTimeout(timeoutId);
                complete(new Error("aborted"));
                return request;
            },
            promise: function () { // <--- added feature!
                return promise;
            }
        };
    });

    return request; // We return the same as in version-1, but with additional promise method
}

// How version 2 is used, by getting the new promise method
let num = 2;
let promise = getAsyncInverse(num).promise();
promise.then(function (result) {
    console.log("The result is:", result);    
}).catch(function (err) {
    console.log("There was an error:", err);
});

Как видите, было бы не очень хорошей идеей опускать объект запроса, и функция возвращала обещание.Это сломало бы существующий код, используя ваш API (обратной совместимости нет).

...