【问题标题】:Rejected Promise breaks loop despite catchRejected Promise 打破循环,尽管 catch
【发布时间】:2021-08-12 17:44:01
【问题描述】:

我正在创建一个循环来使用具有内置承诺的函数来创建/更新用户:

for (const user of usersjson.users) {
 let getuser = getUser(url, okapikey, user[fieldMap.externalSystemId], 
 'externalSystemId'); //Check if user exists on the server
 await getuser
  .then(async (data) => {
    if (data.users.length != 0) { //If user exists in the array
      update = updateUser(url, okapikey, createduser, data.users[0].id);//Create update function
      promises.push(update); //Store function in array
      i++;
    } else {
      create = createNewUser(url, okapikey, createduser);//Create create function 
      promises.push(create); //Store function in array
      i++;
    }
  }).catch((err) => {
    console.error(err);
  });
  if (promises.length == 50 || i == usersjson.users.length) {//Run functions in batches of 50
     await Promise.allSettled(promises)
     .then((responses)=> {
       for (const response of responses) { //For each promise response
         if (response.status == 'fulfilled') { //If fulfilled
           if (response.value.status == 204) {
             console.log(`${response.value.status}: User ${response.value.request.path.substring(7)} was updated.`);
           } else {
             if (response.value.status == 201 && response.value.headers.location) {
               console.log(`${response.value.status}: User ${response.value.headers['location']} was created.`);
             } else {
               console.log(response.value.headers.location);
             }
           }
         } else { //Handle rejections
           console.log(`There was an error with the user:${response.value}`);
         }
       }
     }).catch((err)=> {
       console.log(err);
     });
     promises=[]; //Empty Promise array 
   }
}

async function updateUser(url, token, user, userid)
{
    return new Promise((resolve, reject) => {
        //Create headers for put request
        const options = {
            method: "put",
            headers: {
            'x-okapi-token': token,
            'x-okapi-tenant':'tenant',
            'Content-type':"application/json"
            }
        };
        //Make API get call
        user.id=userid; //Adding the required field ID to the JSON
        axios.put(`${url}/users/${userid}`, JSON.stringify(user), options)
            .then(response => {
              if (response.status == 204) {
                resolve(response);
              } else {
                reject(`Error Code: ${err.response.status}\nError Text: ${err.response.data.errors[0].message}\nError Status: ${err}`);
              }
            }).catch((err) => {
            console.error(`Error Code: ${err.response.status}`);
            if (typeof err.response.data == 'string') {
                console.error(err.response.data);
                reject(`Error Code: ${err.response.status}\nError Text: ${err.response.data.errors[0].message}\nError Status: ${err}`);
            } else if (err.response.data.errors[0].message) {
                console.error(`Error Text: ${err.response.data.errors[0].message}`);
                reject(`Error Code: ${err.response.status}\nError Text: ${err.response.data.errors[0].message}\nError Status: ${err}`);
            } else {
              reject(`Error Code: ${err.response.status}\nError Text: ${err.response.data.errors[0].message}\nError Status: ${err}`);
            }
            console.log(err.response);
            });
    });
};

async function createNewUser (url, token, user) {
    return new Promise((resolve, reject) => {
        //Create headers for put request
        const options = {
            headers: {
            'X-Okapi-token': token,
            'Content-type':"application/json"
            }
        };
        //Make API get call
        axios.post(`${url}/users`, JSON.stringify(user), options)
            .then(response => {
            if (response.status == 201) {
              resolve(response);
            } else {
              reject(`Error Code: ${err.response.status}: ${user.externalSystemId},\nError Text: ${err.response.data.errors[0].message},\nError Status: ${err}`)
            }
            }).catch((err) => {
            console.error(`Error on ${user.externalSystemId}: ${err}`);
            if (err.response.data && typeof err.response.data == 'string') {
                console.error(err.response.data);
                reject(`Error Code: ${err.response.status}: ${user.externalSystemId},\nError Text: ${err.response.data.errors[0].message},\nError Status: ${err}`)
            } else if (err.response.data.errors[0].message) {
                console.error(`Error Text: ${err.response.data.errors[0].message}`);
                reject(`Error Code: ${err.response.status}: ${user.externalSystemId},\nError Text: ${err.response.data.errors[0].message},\nError Status: ${err}`)
            } else {
              reject(`Error Code: ${err.response.status}: ${user.externalSystemId},\nError Text: ${err.response.data.errors[0].message},\nError Status: ${err}`)
            }
            });
    });
};

const getUsers = (url,user,password) =>
{
return new Promise((resolve, reject) => {
  //Create headers for POST request
  const options = {
    method: 'post',
    headers: {
       'Authorization': 'Basic '+Buffer.from(`${user}:${password}`).toString('base64')
    }
  }
  //Make API get call
  axios.get(url, options)
    .then(response => {
      resolve(response.data);
    }).catch((err) => {
      console.error(err);
      reject(err);
    });
});
};

当每一个承诺都被履行时,代码和循环都可以正常工作,但一旦承诺被拒绝,循环就会中断。我收到错误消息,例如:

XXX 错误:错误:请求失败,状态码为 422 错误文本: 具有此用户名的用户已存在 节点:内部/进程/承诺:246 triggerUncaughtException(err, true /* fromPromise */); ^

[UnhandledPromiseRejection:此错误源于抛出 在没有 catch 块的异步函数内部,或通过拒绝 未使用 .catch() 处理的承诺。承诺被拒绝 原因“错误代码:422:XXX,错误文本:具有此用户名的用户 已存在,错误状态:错误:请求失败,状态码 422"。] {}

查看代码和错误,我相信这是来自“createNewUser”函​​数。 我不确定为什么代码会中断 - 我在所有函数中添加了 catch,处理了拒绝,并在代码主体中添加了 catch 语句,但循环仍然中断。

我需要的是让循环像往常一样继续,即使一个函数失败(我稍后会将日志从 console.log 更改为实际的日志文件)。

【问题讨论】:

  • 顺便说一句,避免 updateUser/createNewUser/getUsers 函数中的 Promise constructor antipattern
  • 你误用/误解了awaitlet getUser = getUser(...); await getUser.then(data => ...)let data = await getUser(...); ... 相同,在这种情况下尽量不要将await.then 混合使用
  • @Bergi 既然您在这里提供了帮助并且我实现的代码是您建议的更改,我想也许您可以帮助解决由此引起的奇怪问题。在以前的版本中没有发生这种情况,只有在我将数组更改为您向我展示的函数时:stackoverflow.com/questions/67677866/…

标签: node.js loops promise catch-block


【解决方案1】:
update = updateUser(url, okapikey, createduser, data.users[0].id);//Create update function
promises.push(update); //Store function in array

create = createNewUser(url, okapikey, createduser);//Create create function 
promises.push(create); //Store function in array

这是不准确的。您没有将 functions 存储在该数组中,您实际上在这里调用了 updateUser/createNewUser 函数并将生成的 Promise 存储在数组中。然后,您的循环继续按顺序(因为await)在promises 数组上实际调用Promise.allSettled 之前执行更多getUser 操作。与此同时,一些 Promise 可能已经被拒绝而没有附加任何处理程序。

这与Waiting for more than one concurrent await operationAny difference between await Promise.all() and multiple await?中讨论的问题基本相同。

要修复它,请收集稍后可以在数组中执行的实际函数:

let functions = [];
for (const user of usersjson.users) {
  i++;
  try {
    const data = await getUser(url, okapikey, user[fieldMap.externalSystemId], 'externalSystemId');
    if (data.users.length != 0) {
      functions.push(() =>
//                   ^^^^^
        updateUser(url, okapikey, createduser, data.users[0].id)
      ); // Create update function and store it in array
    } else {
      functions.push(() =>
//                   ^^^^^
        createNewUser(url, okapikey, createduser)
      ); // Create create function and store it in array
    }
  } catch(err) {
    console.error(err);
  }
  if (functions.length == 50 || i == usersjson.users.length) { // in batches of 50
    const promises = functions.map(fn => fn()); // Run functions
//  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    const responses = await Promise.allSettled(promises);
    for (const response of responses) {
      if (response.status == 'fulfilled') {
        if (response.value.status == 204) {
          console.log(`${response.value.status}: User ${response.value.request.path.substring(7)} was updated.`);
        } else {
          if (response.value.status == 201 && response.value.headers.location) {
            console.log(`${response.value.status}: User ${response.value.headers['location']} was created.`);
          } else {
            console.log(response.value.headers.location);
          }
        }
      } else {
        console.log(`There was an error with the user:${response.value}`);
      }
    }
    functions = []; // empty functions array 
  }
}

(我试过avoid awaiting any .then(…) chains

【讨论】:

  • 谢谢!我会立即尝试这样做,看看它是否有效!但在此之前,我对您对 Promise 构造函数反模式的评论感到有些困惑。我查看了您发布的帖子中的答案,但那里的承诺比我的要简单得多,我无法弄清楚我的功能有什么问题。我似乎对每种情况或错误都有拒绝。
  • @NimrodYanai 虽然您似乎正确处理了所有情况(总是调用resolvereject),但您可以通过在then/catch 处理程序,没有任何 new Promise
  • 嗯,我不知道为什么,但这会导致函数停止工作。我按照您的建议进行了更改,现在更新功能中断了,即使我从未接触过它。好消息是 - 即使某些承诺被拒绝,代码确实会继续运行,所以我将称之为解决!谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-02-09
  • 1970-01-01
  • 2021-03-11
  • 2018-02-28
相关资源
最近更新 更多