如何从AWS Lambda node.js异步函数返回数据?

The*_*ack 1 javascript asynchronous callback node.js aws-lambda

我正在研究一些托管在AWS Lambda服务上的脚本.我之所以选择Node.js,是因为我对JS没用,但我还没学过Python或Java.然而,它被证明是一场噩梦,因为我需要查询一个MySQL数据库而我无法弄清楚如何正确地从函数中获取结果.

所以基本上我有这样的东西(我已经削减了一些东西,但你应该得到这个想法),这就是我想要做的.我想能够查询MySQL数据库,当它有答案时,只需返回它,或者如果有错误则抛出.

var mysql = require("mysql");     //npm installed module "mysql"
var err = require("./errors.js"); //internally requires npm installed module "errors"

var main = function(event, context, callback){
  try{
    //Confidential
    var data = null;
    //Confidential
    try{
      data = databaseCommand("SELECT * FROM `<table>` WHERE <field> = <value>");
    }
    catch(e){
      if(e instanceof err.Database)
        //Return state that indicates "Internal server error".
      else
        throw e;
      return
    }
    //Do maths on data
    //Return "OK" and result
  }
  catch(e){
    //Return "Unkown error"
  }
};
var databaseCommand = function(cmdString){
  if(typeof cmdString !== "string") throw new err.InputInvalidType({explanation: "'cmdString' is of type '" + typeof cmdString + "', expected type 'string'"});

  var connection = mysql.createConnection({
    host: process.env.db_host,
    port: process.env.db_port || 3306,
    user: process.env.db_username,
    password: process.env.db_password,
    database: process.env.db_database
  });

  var ret = {
    error: null,
    result: null
  };

  //I cut out the connection.connect() because it can be implied and I'm confused enough
  connection.query(cmdString, function(error, rows){
    if(error)
      ret.error = error;
    else
      ret.result = rows;
  });
  connection.end();

  if(ret.error)
    throw new err.Database();
  return ret.result;
};
Run Code Online (Sandbox Code Playgroud)

但是那些精通Node.js的人显然这不起作用,因为对connection.query的调用是异步的,所以我的databaseCommand函数总是返回null(并且不抛出)并导致我的main函数出错.

请帮助我理解如何执行这样的基本同步请求.

编辑

我已经看到使用异步方法的"解决方案",显示类似(我可能有这个错误)的以下更改,但我不知道这有什么不同.

var mysql = require("mysql");     //npm installed module "mysql"
var err = require("./errors.js"); //internally requires npm installed module "errors"

var main = function(event, context, callback){
  try{
    //Confidential
    var data = null;
    //Confidential
    try{
      databaseCommand("SELECT * FROM `<table>` WHERE <field> = <value>", function(err, result){
        if(err)
          throw err;
        data = result;
      });
      //This function will still return before data is set
      //Maths will still be performed before data is set
    }
    catch(e){
      if(e instanceof err.Database)
        //Return state that indicates "Internal server error".
      else
        throw e;
      return
    }
    //Do maths on data
    //Return result
  }
  catch(e){
    //Return "Unkown error"
  }
}
var databaseCommand = function(cmdString, callback){
  if(typeof cmdString !== "string") throw new err.InputInvalidType({explanation: "'cmdString' is of type '" + typeof cmdString + "', expected type 'string'"});

  var connection = mysql.createConnection({
    host: process.env.db_host,
    port: process.env.db_port || 3306,
    user: process.env.db_username,
    password: process.env.db_password,
    database: process.env.db_database
  });

  var ret = {
    error: null,
    result: null
  };

  //I cut out the connection.connect() because it can be implied and I'm confused enough
  connection.query(cmdString, function(error, rows){
    if(error)
      callback(err, null);
    else
      callback(null, rows);
  });
  connection.end();
}
Run Code Online (Sandbox Code Playgroud)

Vla*_*iev 8

看起来您缺少JavaScript中回调的基本概念.

您必须使用callback函数中提供的参数main.这就是你如何告诉Lambda你有一些结果可以返回.

以下是您的代码的简化版本,以便您了解:

var mysql = require("mysql"); //npm installed module "mysql"
var err = require("./errors.js"); //internally requires npm installed module "errors"

var connection;

var main = function(event, context, callback) {
    databaseCommand("SELECT * FROM `<table>` WHERE <field> = <value>", (error, rows) => {
        if (error) return callback(error);

        var results = doSomeMathWithRows(rows);

        callback(null, results);
    });
};

var databaseCommand = function(cmdString, callback) {
    if (typeof cmdString !== "string") throw new err.InputInvalidType({
        explanation: "'cmdString' is of type '" + typeof cmdString + "', expected type 'string'"
    });

    // Don't init DB connection for every request
    // Lambda functions can lose it only after freezing (see docs for details when)
    // so we create connection only on demand
    if (!connection) {
        connection = mysql.createConnection({
            host: process.env.db_host,
            port: process.env.db_port || 3306,
            user: process.env.db_username,
            password: process.env.db_password,
            database: process.env.db_database
        });
    }

    connection.query(cmdString, callback);
};
Run Code Online (Sandbox Code Playgroud)

注意a callback作为调用的最后一个参数databaseCommand.这意味着什么时候connection.query从DB获取一些行,它将调用Lambda提供的相同回调.

另一个重点是不要在每个Lambda执行上创建数据库连接.它的价格昂贵.您可以初始化变量一次,并在函数冻结时再次初始化它.在这里阅读更多相关信息 - https://aws.amazon.com/blogs/compute/container-reuse-in-lambda/

让我知道它是否适合你,因为我在网上编辑了这段代码而没有任何检查.但希望你明白这一点.您可以在这里阅读更多如何编写处理函数:http://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html?shortFooter=true