首页 > 解决方案 > 在将对象的错误数组推送到 CSV 之前等待 promise 完成

问题描述

我正在尝试从 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);
  });

标签: javascriptnode.jspromiseokta-apicsv-write-stream

解决方案


您的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))

推荐阅读