简体   繁体   English

如何在 lambda 函数中按顺序使用 async/await?

[英]How to use async/await sequentially inside lambda function?

I am creating a Lambda Function which gets data from s3 bucket and stream it to fast-csv for parsing.我正在创建一个 Lambda 函数,它从 s3 存储桶获取数据并将其流式传输到 fast-csv 以进行解析。 After that, I need to connect to documentDB database to send those parsed data.之后,我需要连接到 documentDB 数据库来发送这些解析的数据。

But the problem is that sometimes the database connection function runs before the parse function and throws blank array and parsed function dont run sometime or vice-versa.但问题是有时数据库连接函数在解析函数之前运行并抛出空白数组和解析函数有时不运行,反之亦然。

So, how can I run the parse function (parserFcn function) always before the database connection and send function (connectToDb function) so that it can get data from the parse function.那么,如何始终在数据库连接和发送函数(connectToDb 函数)之前运行解析函数(parserFcn 函数),以便它可以从解析函数中获取数据。

Here is the code -这是代码 -

const AWS = require("aws-sdk");
const fs = require("fs");
const csv = require("@fast-csv/parse");
const MongoClient = require("mongodb").MongoClient;

const s3 = new AWS.S3();

exports.handler = async (event, context, callback) => {
  const bucketName = event.Records[0].s3.bucket.name;
  const keyName = event.Records[0].s3.object.key;

  console.log("Bucket Name->", JSON.stringify(bucketName));
  console.log("Bucket key->", JSON.stringify(keyName));

  var params = {
    Bucket: bucketName,
    Key: keyName,
  };
  var parsedData = [];
  const s3Contents = s3.getObject(params).createReadStream();

  let parserFcn = new Promise((resolve, reject) => {
    const parser = csv
      .parseStream(s3Contents, { headers: true })
      .on("data", function (data) {
        parsedData.push(data);
      })
      .on("end", (rowCount) => {
        console.log(`Parsed ${rowCount} rows`);
        resolve(parsedData);
      })
      .on("error", function () {
        reject("csv parse process failed");
      });
    return parser;
  });

  let connectToDb = new Promise((resolve, reject) => {
    var client = MongoClient.connect(
      "mongodb://user:pass@host/?ssl=true&retryWrites=false",
      {
        tlsCAFile: `/opt/rds-combined-ca-bundle.pem`, //Specify the DocDB; cert
      },
      function (err, client) {
        if (err) {
          throw err;
        } else {
          console.log("connected ");
        }
        console.log("parsedData inside conn ", parsedData);

        // Specify the database to be used
        db = client.db("database-name");

        // Specify the collection to be used
        col = db.collection("collection-name");

        // Insert Multiple document
        col.insertMany(parsedData, function (err, result) {
          if (err) {
            console.log("error->", err);
          }
          console.log("Result from db->", result);

          //Close the connection
          client.close();
        });
      }
    );
    return client;
  });

  const parserdata = await parserFcn;
  const conn = await connectToDb;

  let promiseFactories = [parserdata, conn];

  Promise.all(promiseFactories).then((data) => {
    console.log("completed all promises", data);
  });
};

Here's an attempt at replacing promise definitions in the post with functions that return a promise as suggested by @Gamma032.这是尝试用@Gamma032 建议的返回承诺的函数替换帖子中的承诺定义。 You may find it useful as a guide to compare with code you are writing and what the handler is supposed to do.您可能会发现它可用作与您正在编写的代码以及处理程序应该做什么进行比较的指南。

The replacement functions were not declared as async functions because they're using callbacks to resolve/reject the new promises they create and return.替换函数未声明为async函数,因为它们使用回调来解析/拒绝它们创建和返回的新承诺。 Waiting for functions to complete in the order called is performed inside a standard try/catch so that code can detect await re-throwing the rejection reason of a rejected promise it was waiting on.等待函数按调用顺序完成是在标准try/catch中执行的,以便代码可以检测await重新抛出它正在等待的被拒绝承诺的拒绝原因。

I left the global variables mentioned in comment as they were, but moved the initial definition of parsedData inside the parseData function and renamed the "connection" function to updateDB because it both connects to and updates the database.我保留了注释中提到的全局变量,但将parseData parsedData中,并将“连接”函数重命名为updateDB ,因为它既连接到数据库又更新了数据库。

const AWS = require("aws-sdk");
const fs = require("fs");
const csv = require("@fast-csv/parse");
const MongoClient = require("mongodb").MongoClient;

const s3 = new AWS.S3();

exports.handler = async (event, context, callback) => {
  const bucketName = event.Records[0].s3.bucket.name;
  const keyName = event.Records[0].s3.object.key;

  console.log("Bucket Name->", JSON.stringify(bucketName));
  console.log("Bucket key->", JSON.stringify(keyName));

  var params = {
    Bucket: bucketName,
    Key: keyName,
  };
  const s3Contents = s3.getObject(params).createReadStream();
  
  function parseData() {
    return new Promise((resolve, reject) => {
      const parsedData = [];
      csv.parseStream(s3Contents, { headers: true })
        .on("data", function (data) {
          parsedData.push(data);
        })
        .on("end", (rowCount) => {
          console.log(`Parsed ${rowCount} rows`);
          resolve(parsedData);
        })
        .on("error", function () {
          reject("csv parse process failed");
        });
    });
  }

  function updateDB(parsedData) {
    console.log("parsedData inside updateDB ", parsedData);
    return new Promise((resolve, reject) => {
      var client = MongoClient.connect(
        "mongodb://user:pass@host/?ssl=true&retryWrites=false",
        {
          tlsCAFile: `/opt/rds-combined-ca-bundle.pem`, //Specify the DocDB; cert
        },
        function (err, client) {
          if (err) {
            console.error( "connection failure");
            reject(err);
            return; // error return
          }
          console.log("connected ");
          // Specify the database to be used
          db = client.db("database-name");

          // Specify the collection to be used
          col = db.collection("collection-name");

          // Insert Multiple document
          col.insertMany(parsedData, function (err, result) {
            if (err) {
              console.error( "insertion failure");
              reject( err);
            } else {
              resolve( result);
           // Close the connection
           client.close();
            } 
          });

          
        });
      }
    );
  }
  
  // call parseData and updateDB in order
  try {
    const parsedData = await parseData();
    const result = await updateDB(parsedData);
    console.log( "DB updated with result", result);

    // see note:
    // const promiseFactories = [parsedData, result];
    //Promise.all(promiseFactories).then((data) => {

     console.log("completed all promises", data);

    // });
  }
  catch(err) {
     console.error( err); // there may already be a line on the console about the error.
  }
}

  
};

Note笔记

An edit from the OP added添加了来自 OP 的编辑

    const promiseFactories = [parsedData, result];
    Promise.all(promiseFactories).then((data) => {
     console.log("completed all promises", data);
    });

to the try clause after awaiting the values of parsedData and result .在等待parsedDataresult的值后到try子句。 However neither of these values in a promise (you can't fulfill a promise with a promise and the await operator never returns a promise as the result of the await operation), so passing them through a call to Promise.all simply puts a job in the promise job queue to perform the console.log from the then handler.然而,promise 中的这些值都不是(你不能用一个 promise 来实现一个 promise,并且await操作符永远不会返回一个 promise 作为 await 操作的结果),所以通过调用Promise.all传递它们只是简单地放置了一个工作在 promise 作业队列中从then处理程序执行 console.log。 Logging the message after awaiting both values should suffice.在等待两个值之后记录消息就足够了。

You should await functions that return promises, not variables that hold promises.您应该await返回承诺的函数,而不是持有承诺的变量。

Declaring let parserFcn = new Promise(...) and let connectToDb = new Promise(...) starts the parsing and database connection, with no guarantees on execution order.声明let parserFcn = new Promise(...)let connectToDb = new Promise(...)开始解析和数据库连接,不保证执行顺序。

So declare two functions:所以声明两个函数:

  1. parserFcn , which returns a promise to the parsed data array. parserFcn ,它向已解析的数据数组返回一个承诺。
  2. connectToDb , which takes the parsed data and pushes it to the database. connectToDb ,它获取解析的数据并将其推送到数据库。

Then just call them in order:然后按顺序调用它们:

const parsedData = await parserFn()
await connectToDb(parsedData)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM