【问题标题】:I want my aws lamda to not return anything until the mysql query has been executed completely on node.js我希望我的 aws lambda 在 node.js 上完全执行 mysql 查询之前不返回任何内容
【发布时间】:2020-05-17 16:42:58
【问题描述】:

我将首先提供代码,它是一个 AWS node.js Lambda 函数

    const mysql = require('mysql');
       const con = mysql.createConnection({
        host: 'testtest.ap-southeast-2.rds.amazonaws.com',
        user: 'test',
        password: 'test',
        database: 'test',
    });      
    exports.handler = function(event,context,callback){
        context.callbackWaitsForEmptyEventLoop = false;
        var queryResult=[];
        var searchbyArray = (event.searchby);
        var len = searchbyArray.length;
        getResult(len,searchbyArray,function(err,data){
            if(err){console.log("the error is "+err);}
            else{
                callback(null,data);
            }
        });
};

function getResult(len,searchbyArray,cb){
    var results=[];
    for(var i=0; i<len; i++){
        console.log("before loop"+i);
        var sql ="SELECT * FROM aa_customer_device WHERE id LIKE '%"+searchbyArray[i]+"%'";
        con.query(sql,function(err,result){
        if (err){cb(err,null);}
        else{
            results.push(result);
        }
      });
    }
   cb(null,results);
}

在这里,cb(null, results) 在 for 循环完成之前被执行,因为 con.query(...) 是一个异步函数(猜猜这就是它的调用)并且总是在 'results 中返回一个空数组' 然后由处理程序在其回调函数中返回。

有什么办法可以在 node.js 中编写代码,使其遵循像

这样的顺序
  • 在 for 循环中完成所有 con.query(...) 的执行
  • 将每次循环迭代的结果数组推送到“结果”数组
  • 只有在上述两个步骤完成后调用 cb(null,results)

这里发生的是 cb(null, results) 在 for 循环有机会将所需信息存储在结果数组中之前执行的方式。

有什么方法可以使用 node.js 完成我想要的。有人建议我使用 Promise、async、await,但形成我阅读的所有这些解决方案并不能完全解决我的问题。

【问题讨论】:

  • for(var i=0; i
  • 它的 searchbyArray.length... 我在调用 getResult() 时将 len 作为参数传递了

标签: node.js asynchronous aws-lambda


【解决方案1】:

您已经遇到了问题。 cb(null , results) 不等待查询完成。为了让它等待,正如你已经提到的,你可以使用 promise/async-await。

这是您如何编写async/await 代码来实现您想要做的事情的方法。

const mysql = require('mysql');
const promisify = require('util').promisify;

const con = mysql.createConnection({
  host: 'testtest.ap-southeast-2.rds.amazonaws.com',
  user: 'test',
  password: 'test',
  database: 'test',
});

// con.connect uses a callback, we can use util.promify to convert it to promise driven
const connect = promisify(con.connect);

// con.query uses a callback, we can use util.promify to convert it to promise driven
const query = promisify(con.query);

exports.handler = async function (event, context) {
  context.callbackWaitsForEmptyEventLoop = false;
  const queryResult = [];
  const searchbyArray = (event.searchby);
  const len = searchbyArray.length;

  try {

    // wait for the connection to be established.
    await connect();

    const data = getResult(len, searchbyArray);

    // return the lambda success result
    return {
      statusCode: 200,
      responseBody: JSON.stringify(data)
    }

  } catch(err) {
    console.log("the error is " + err)

    // return the lambda failure result
    return {
      statusCode: 500,
      responseBody: err.message
    }
  }
};

async function getResult(len, searchbyArray, cb) {
  const results = [];
  for (const i = 0; i < len; i++) {
    console.log("before loop" + i);

    const sql = `SELECT * FROM aa_customer_device WHERE id LIKE '%${searchbyArray[i]}%'`;

    // wait for the query to complete
    const results = await query(sql);
    results.push(result);
  }
  return results;
}

希望这会有所帮助,如果您不理解此代码,请随时问我。

【讨论】:

  • 这就是你要找的东西
  • 我确实尝试过使用你的代码,'const results = await query(sql);' 行似乎有某种错误无法完全记住错误是什么。 @slebetman 回答对我有用,我现在已经接受它作为批准的答案。
【解决方案2】:

您需要手动检测最后一个请求何时完成:

function getResult(len,searchbyArray,cb){
    var results=[];
    var requestCount = 0;
    var error = null;

    for(var i=0; i<len; i++){

        requestCount++; // count the requests made

        var sql ="SELECT * FROM aa_customer_device WHERE id LIKE '%"+searchbyArray[i]+"%'";
        con.query(sql,function(err,result){
            // Note: All logic needs to happen here since
            //       this is the ONLY place where code gets
            //       executed in the FUTURE instead of now.

            requestCount --; // count completed requests

            if (err){error = err}
            else{
                results.push(result);

                if (requestCount === 0) {
                    if (error) cb(error,null);   // "return" error
                    else       cb(results);      // "return" results
                }
            }
        });
    }
}

使用异步/等待

如果您可以访问 ES6 环境,则可以使用 async/await 使这更易于阅读。首先,您需要承诺查询:

// Promisify con.query:
function query (con, sql) {
    return new Promise((resolve, reject) => {
        con.query(sql, (err, result) => {
            if (err) reject(err);
            else     resolve(result);
        });
    });
}

async function getResult(len,searchbyArray,cb){
    var results=[];

    for(var i=0; i<len; i++){
        var sql ="SELECT * FROM aa_customer_device WHERE id LIKE '%"+searchbyArray[i]+"%'";

        try {
            var result = await query(con, sql);
            results.push(result);
        }
        catch (err) {
            cb(err,null);
            return; // stop processing
        }
    }
    cb(results);
}

【讨论】:

    【解决方案3】:

    原因是con.query()是一个回调函数。回调是异步的,这意味着它不会按顺序执行。你有两个选择。

    • 使用 await 关键字(如果可能)
    • 使用 Promise 包装函数

    我实际上不知道 AWS Lambda Mysql 支持 async/await,因此我将演示第二个选项。这两个选项做同样的事情,但模式不同。

    这里的情况是回调异步执行。在结果出来之前它不会“等待”,这就是你得到一个空数组的原因。这就是异步函数的行为方式。如果你要在 Node js 或 Java 脚本中开发甚至没有服务器的应用程序,这是一个非常重要的主题。请如果您需要更多解释,请发表评论。

    在这里你可以使用 Promise 包装 con.query() 函数。

           const con = mysql.createConnection({
            host: 'testtest.ap-southeast-2.rds.amazonaws.com',
            user: 'test',
            password: 'test',
            database: 'test',
        });      
        exports.handler = async (event,context,callback)=>{
            context.callbackWaitsForEmptyEventLoop = false;
            var queryResult=[];
            var searchbyArray = (event.searchby);
            var len = searchbyArray.length;
            try{
                const result = await Promise.all(getResult(searchbyArray));
            }catch (error){
                console.log("the error is "+err);
            }
    };
    
    const getResult = async (searchbyArray) =>{
        const result = searchbyArray.map(searchItem=>{
            var sql =`SELECT * FROM aa_customer_device WHERE id LIKE %${searchItem}%`;
            return new Promise((resolve, reject) => {
                con.query(sql, (err, result) => {
                    if (err) 
                        reject(err);
                    else     
                        resolve(result);
                });
            });
        })
        return result;
    }
    

    我已将 for 循环替换为 map 而不是 push to array 并使用了 Promise。所有此应用程序将运行得更快,而不是在每个循环周期中等待。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-06-24
      • 1970-01-01
      • 2019-06-23
      • 1970-01-01
      • 2013-09-10
      • 1970-01-01
      • 2021-01-11
      相关资源
      最近更新 更多