【问题标题】:Wait for promises to complete before pushing error array of objects to CSV在将对象的错误数组推送到 CSV 之前等待 promise 完成
【发布时间】:2020-10-09 19:18:03
【问题描述】:

我正在尝试从 Okta 导入和删除大量用户,同时保持在速率限制内,并将任何错误记录到 Excel 中。下面的代码似乎可以正常工作,但问题是我在控制台日志中看到的 5 个错误中的最后一个错误没有出现在输出的 CSV 中。

我尝试了一系列替代方法,包括将 csvWriter 调用放在 .then 而不是 .finally 中。问题是它没有等待最后一个错误被推送到数组。

"use strict";
const okta = require("@okta/okta-sdk-nodejs");
const csv = require("csv-parser");
const fs = require("fs");
const createCsvWriter = require("csv-writer").createObjectCsvWriter;

let timeRun = new Date()
  .toISOString()
  .replace(/T/, " ") // replace T with a space
  .replace(/\..+/, "") // delete the dot and everything after
  .replace(/:/g, "."); // replace T with a space

const csvWriter = createCsvWriter({
  path: "errorLog-" + timeRun + ".csv",
  header: [
    { id: "error", title: "Error" },
    { id: "row", title: "Row" },
  ],
});
let record = [];
let currentError = {}

// Enter tenant info and API key here
const client = new okta.Client({
  orgUrl: "https://xxxxxxxx.oktapreview.com",
  token: "xxxxxxxxxxxxxxxxxxxxxxx",
});

let usersToDelete = [];
let currentUserID;

var getUsersToDelete = new Promise((resolve, reject) => {
  fs.createReadStream("testImport.csv")
    .pipe(csv())
    .on("data", (row) => {
      usersToDelete.push(row);
    })
    .on("end", (row) => {
      resolve();
    });
});

getUsersToDelete
  .then(async () => {
    let iCount = 1;
    while (usersToDelete.length > 0) {
        
      var deleteUserTimeout = new Promise((resolve, reject) => {
        setTimeout(async function () {
          currentUserID = usersToDelete.pop();
          client
            .getUser(currentUserID.email)
            .then(async (user) => {
              return user
                .deactivate()
                .then(() => console.log("User has been deactivated"))
                .then(() => user.delete())
                .then(() => console.log("User has been deleted"));
            })
            .catch(async function (error, row) {
                    currentError = { error: error.message, row: "row" };
                    console.error(currentError);
              return error;
            })
            .finally(() => {
              record.push(currentError);
              reject()
            });
          resolve();
        }, 2000);
      });
      await deleteUserTimeout;
      console.log("Timeout" + currentUserID, "Iteration: " + iCount);
      iCount++
    }
  }).finally(async () => {
    await csvWriter.writeRecords(record);
  });

【问题讨论】:

    标签: javascript node.js promise okta-api csv-write-stream


    【解决方案1】:

    您的deleteUserTimeout 承诺不会等待用户删除操作完成,并且脚本在启动setTimeout 回调后几乎立即完成最后一个要删除的用户。 CSV 将在该时间写入 record 中累积的所有错误,而不是等待某些操作完成,包括遇到错误的 5 次迭代。

    基本思想与@teppic 的相同,但您可能希望在发现时写出每个错误,而不是将它们延迟到最后,以减少在脚本崩溃时丢失错误信息的可能性原因(例如机器断电)。

    const sleep = time => new Promise(resolve => setTimeout(resolve, time))
    
    const deleteUser = async email => {
      const user = await client.getUser(email)
      await user.deactivate()
      await user.delete()
    }
    
    const deleteUsers = async users => {
      for (const user of users) {
        try {
          await deleteUser(user.email)
          console.log(`Deleted ${user.email}`)
          await sleep(2000)
        } catch (e) {
          console.error(e.message)
          await csvWriter.writeRecords([{error: e.message, row: 'row'}])
        }
      }
    }
    
    getUsersToDelete.then(() => deleteUsers(usersToDelete))
    

    【讨论】:

      【解决方案2】:

      您的deleteUserTimeout 承诺中存在竞争。它在client.getUser 之后立即解决,无需等待结果。

      var deleteUserTimeout = new Promise((resolve, reject) => {
        setTimeout(async function () {
          currentUserID = usersToDelete.pop();
          client.getUser(currentUserID.email) // forgot to await this
          //... snip
          resolve(); // resolves immediately
        }, 2000);
      });
      await deleteUserTimeout; // this won't wait for the delete
      

      为了清晰起见,我会对其进行重构(未经测试):

      async function deleteUser(email) {
        let user = await client.getUser(email);
        await user.deactivate();
        await user.delete();
      }
      
      function sleep(timeoutMs) {
        return new Promise(_ => setTimeout(_, timeoutMs));
      }
      
      async function deleteUsers(usersToDelete) {
        const errors = [];
      
        for (let row = 0; row < usersToDelete.length; row++) {
          let currentUserID = usersToDelete[row];
          await sleep(2000);
          await deleteUser(currentUserID.email)
            .catch(error => {
              errors.push({error: error.message, row});
            });
        }
      
        return errors;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-03-22
        • 1970-01-01
        • 2023-03-25
        • 1970-01-01
        • 1970-01-01
        • 2015-03-01
        • 1970-01-01
        • 2015-07-01
        相关资源
        最近更新 更多