NodeJS Lambda 从 promise 内部返回结果

huangapple go评论71阅读模式
英文:

NodeJS Lambda Return result from inside a promise

问题

我有一个使用mysql库来选择数据并将其作为结果检索的NodeJS Lambda函数。它通过API Gateway的HTTP调用触发。

但我似乎找不到一种将查询结果作为调用的响应返回的方法。

这是示例代码:

import { createRequire } from 'module';
const require = createRequire(import.meta.url);

const mysql = require('mysql');

const con = mysql.createConnection({
  host: process.env.RDS_HOSTNAME,
  user: process.env.RDS_USERNAME,
  password: process.env.RDS_PASSWORD,
  port: process.env.RDS_PORT,
  database: process.env.RDS_DATABASE,
});

const tableName = 'fit_guide';

export const handler = async (event, context, callback) => {
  let response = {};
  const routeKey = event.routeKey;

  let sql = `SELECT * FROM ${tableName}`;
  con.query(sql, function (error, results, fields) {
    if (error) throw error;
    // 这里会打印数据库表的结果
    console.log('******** ', results);
    // 但这里不会返回结果
    response = {
      statusCode: 200,
      success: true,
      results,
    };
    callback(null, JSON.stringify(response));
    return response;
  });
  con.end();

  // 这里会返回一个空对象 {}(初始值)
  callback(null, JSON.stringify(response));
  return response;
};

如何在查询执行中返回响应,而不是对象的初始值?

英文:

I have a NodeJS lambda function that uses the mysql library for selecting data and retrieve it as a result. It gets triggered via an API Gateway http call.

But I can't seem to find a way to return the result of the query as the response of the call.

This is the sample code:

import { createRequire } from 'module';
const require = createRequire(import.meta.url);

const mysql = require('mysql');

const con = mysql.createConnection({
  host: process.env.RDS_HOSTNAME,
  user: process.env.RDS_USERNAME,
  password: process.env.RDS_PASSWORD,
  port: process.env.RDS_PORT,
  database: process.env.RDS_DATABASE,
});

const tableName = 'fit_guide';

export const handler = async (event, context, callback) => {
  let response = {};
  const routeKey = event.routeKey;

  let sql = `SELECT * FROM ${tableName}`;
  con.query(sql, function (error, results, fields) {
    if (error) throw error;
    // THIS DOES PRINT THE RESULTS FROM THE DATABASE TABLE
    console.log('******** ', results);
    // THIS DOESN'T RETURN
    response = {
      statusCode: 200,
      success: true,
      results,
    };
    callback(null, JSON.stringify(response));
    return response;
  });
  con.end();

  // THIS RETURNS AN EMPTY OBJECT {} (initial value)
  callback(null, JSON.stringify(response));
  return response;
};

How can I return the response inside the query execution rather than the initial value of the object?

答案1

得分: 1

您的处理程序存在竞态条件。return response; 行在查询和 Lambda callback 有机会执行之前返回了{}。最简单的修复方法是移除 return response;

这个竞态条件存在是因为您的代码混合了 Lambda 非异步(回调,没有返回值)和异步(没有回调,返回Promise<Something>)处理程序模式。如当前编写的,返回值在竞争中胜出了回调。您应该只使用一种模式,而不是同时使用两种。

以下是一个类似于您的竞态条件的演示(不要在家里尝试这样做!)。如当前编写的,消息将是 Callback wins!,因为回调会在短时间延迟后触发,而 Promise 只在1秒后解析。添加2秒的回调延迟将得到 Promise return wins!。当然,修复方法是移除其中一个,除非您真的喜欢竞赛。

export const raceHandler = async (
  context: Context,
  event: APIGatewayProxyEventV2,
  callback: APIGatewayProxyCallbackV2
): Promise<APIGatewayProxyResultV2> => {
  
  // Race contestant 1:  callback with delay - simulates your MySQL query
  setTimeout(() => {
    callback(null, {
      statusCode: 200,
      body: JSON.stringify({ message: "Callback wins!" }),
    });
  }, 10);

  // Race contestant 2:  Promise return resolution with delay
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({
        statusCode: 200,
        body: JSON.stringify({ message: "Promise return wins!" }),
      });
    }, 1000);
  });
};
英文:

TL; DR Your handler has a race condition. The return response; line returns {} before your query and the Lambda callback have a chance to execute. The easist fix is to remove return response;.


The race condition exists because your code mixes the Lambda Non-Async (callback, no return value) and Async (no callback, return Promise<Something>) handler patterns. As currently written, the return value is winning the race against the callback. You should use one pattern or the other, not both.

Here is a demo with a race condition like yours (Don't try this at home, kids!). As written, the message will be Callback wins! because the callback fires with a short delay, while the Promise resolves only after 1 second. Add a 2 second callback delay to get Promise return wins!. Of course, the fix is to remove one or the other, unless you really enjoy a race.

export const raceHandler = async (
  context: Context,
  event: APIGatewayProxyEventV2,
  callback: APIGatewayProxyCallbackV2
): Promise<APIGatewayProxyResultV2> => {
  
  // Race contestant 1:  callback with delay - simulates your MySQL query
  setTimeout(() => {
    callback(null, {
      statusCode: 200,
      body: JSON.stringify({ message: "Callback wins!" }),
    });
  }, 10);

  // Race contestant 2:  Promise return resolution with delay
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({
        statusCode: 200,
        body: JSON.stringify({ message: "Promise return wins!" }),
      });
    }, 1000);
  });
};

huangapple
  • 本文由 发表于 2023年1月10日 13:21:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/75065782.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定