使用async/await和try/catch实现分层API调用

4

大家好!

我需要调用一个API;如果失败了,我需要使用不同的参数再次调用相同的API;如果再次失败,我需要使用第三个不同的参数再次调用相同的API;如果最终失败,那么就是实际错误并且可以退出。

我能想到的唯一方法是使用嵌套的try/catch语句,例如:

const identityCheck = async (slug) => {
  let res;
  try {
    res = await Bundle.sdk.find(slug);
  } catch (err) {
    console.log('Fragment didn\'t work ========', slug, err);

    try {
      res = await Bundle.sdk.find(`package/${slug}`);
    } catch (e) {
      console.log('Fragment didn\'t work package ========', e);

      try {
        res = await Bundle.sdk.find(`${slug}-list`);
      } catch (error) {
        console.log('None of the fragments worked================.', error);
      }
    }
  }

  return logResponse(res);
};

identityCheck('fashion');

但似乎必须有另一种更简单的方法来做这件事。我尝试将其简化为重试函数,但这只会导致代码量更多、更不清晰:

const identityCheck = (slug) => {
  const toTry = [
    slug,
    `package/${slug}`,
    `${slug}-list`
  ];

  return new Promise((resolve, reject) => {
    let res;
    let tryValIndex = 0;

    const attempt = async () => {
      try {
        res = await Bundle.sdk.find(toTry[tryValIndex]);
        return resolve(logResponse(res));
      } catch (err) {
        console.log(`toTry ${toTry[tryValIndex]} did not work ========`, slug, err);

        if (tryValIndex >= toTry.length) {
          return reject(new Error('Everything is broken forever.'));
        }

        tryValIndex++;
        attempt();
      }
    };

    attempt();
  });
};

需要您的指导和意见!

2个回答

3
避免使用Promise构造函数反模式,并使用参数替代外部作用域变量进行递归计数:
function identityCheck(slug) {
  const toTry = [
    slug,
    `package/${slug}`,
    `${slug}-list`
  ];
  async function attempt(tryIndex) {
    try {
      return await Bundle.sdk.find(toTry[tryIndex]);
    } catch (err) {
      console.log(`toTry ${toTry[tryIndex]} did not work ========`, slug, err);
      if (tryIndex >= toTry.length) {
        throw new Error('Everything is broken forever.'));
      } else {
        return attempt(tryIndex+1);
      }
    }
  }
  return attempt(0);
}

哦,注意到了!这是指您认为此版本比嵌套的try/catch更好,还是一般有帮助的信息? - megkadams
是的,它可以更好:如果需要更多的 toTry 值(只需将它们添加到数组中),则更容易扩展,并且如果代码中的其他地方需要类似的重试逻辑,则进一步抽象可能会有用。如果这两个原因都不重要,那么就取决于简单与抽象的偏好。 - Bergi
谢谢您的快速反馈,太棒了!我可能会等待一些额外的闲聊,然后再接受答案。 :) - megkadams
catch()中应该使用console.error()而不是console.log()吗? - guest271314
@guest271314 不会啊,为什么会呢? - Bergi
显示剩余2条评论

0

继Bergi的答案之后,尝试保留原始结构以避免“更多代码”:

const idCheck = async (slug, alsoTry = [`package/${slug}`, `${slug}-list`]) => {
  let res;
  try {
    res = await Bundle.sdk.find(slug);
  } catch (err) {
    if (!alsoTry.length) throw err;
    return idCheck(alsoTry.shift(), alsoTry);
  }
  return logResponse(res);
};

idCheck('fashion');

这利用了默认参数的强大功能。

同样的复杂度,但美学上更接近嵌套的 try 块,可能是一个更简单的模式。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接