【问题标题】:Safely stop execution of sequential `await`s without handling thrown errors (js async/await conventions)在不处理抛出错误的情况下安全地停止执行顺序`await`(js async/await 约定)
【发布时间】:2021-08-14 00:45:11
【问题描述】:

当我需要进行两次连续的 api 调用时,我更喜欢每个函数来处理它们负责的错误。我一直是这样写的:

class Api {
  static call(path) {
    ...
    return Promise.reject({ code: 400/422/... })
  }
}

async function closeMonkeyCages() {
  try {
    await Api.call('monkeys/close')
  } catch (err) {
    if (err.code === 400) { alert('monkeys arent in the cages yet') }
    throw err
  }
}

async function feedTigers() {
  try {
    boom() // example ReferenceError
    await Api.call('tigers/feed')
  } catch (err) {
    if (err.code === 422) { alert('you must pet them first') }
    throw err
  }
}

(async () => {
  try {
    await closeMonkeyCages()
    await feedTigers()
  } catch (err) {
    // do nothing, closeMonkeyCages + feedTigers should have already shown alerts
  }
})()

但是,此代码很危险。它吞噬了其他错误。如果我的 try 块中有 ReferenceError/TypeError/etc,我希望它被记录(即:Sentry)。

我可以想出几种方法来解决这个问题,但它们看起来要么很乱,要么有点错误,要么不灵活。

  • async 函数可以返回 api 异常而不抛出,类似于 fetch api:
const resp = await closeMonkeyCages()
if (!resp.ok) return
await feedTigers()
  • 在我的主块中处理 api 异常 (alert()):
try {
  await closeMonkeyCages()
  await feedTigers()
} catch (err) {
  if (err?.code === 400) { // conditions that map to each error. messy. error-prone
    alert(getErrorMsg(err)) // hope this function handles all cases
  } else {
    // must have been another runtime error
    throw err
  }
}

  • 类型检查错误:
try {
  await closeMonkeyCages()
  await feedTigers()
} catch (err) {
  if (err instanceof Error) {
    throw err
  }
  // do nothing, closeMonkeyCages + feedTigers should have already shown alerts
}

您将如何保持代码的清洁和安全,同时将错误限制在调用它们的函数范围内?

【问题讨论】:

    标签: javascript error-handling async-await try-catch


    【解决方案1】:

    是的,您应该只处理主程序中的错误,而不是单个函数中的alert,然后继续处理,因为什么都没发生 - 您仍然需要closeMonkeyCages 抛出异常,这样您就永远不会开始喂老虎了没有关上笼子。不过,您仍然可以在相应的函数中保留相应的错误消息:

    class ApiError extends Error {
      constructor(msg, cause) {
        super(msg);
        this.cause = cause;
      }
    }
    
    async function closeMonkeyCages() {
      try {
        await Api.call('monkeys/close')
      } catch (err) {
        if (err.code === 400) throw new ApiError('monkeys arent in the cages yet', err)
        throw err
      }
    }
    
    async function feedTigers() {
      try {
        boom() // example ReferenceError
        await Api.call('tigers/feed')
      } catch (err) {
        if (err.code === 422) throw new ApiError('you must pet them first', err)
        throw err
      }
    }
    
    try {
      await closeMonkeyCages()
      await feedTigers()
    } catch (err) {
      if (err instanceof ApiError) {
        alert(err.message)
        console.log(`${err.message} caused by`, err.cause);
      } else {
        throw err
      }
    }
    

    这与async/await无关,它与同步代码中的错误处理约定完全相同。

    【讨论】:

      【解决方案2】:

      如果我正确理解您所需的逻辑流程,如果您处理 400 条件,您可以throw false

      检查err 是否在最终捕获中不是false

      async function closeMonkeyCages() {
        try {
          await Api.call('monkeys/close')
        } catch (err) {
          if (err.code === 400) { 
            alert('monkeys arent in the cages yet') 
            throw false
          }
          throw err
        }
      }
      
      async function feedTigers() {
        try {
          boom() // example ReferenceError
          await Api.call('tigers/feed')
        } catch (err) {
          if (err.code === 422) { 
            alert('you must pet them first') 
            throw false
          }
          throw err
        }
      }
      
      (async () => {
        try {
          await closeMonkeyCages()
          await feedTigers()
        } catch (err) {
          if(err === false) {
            // do nothing, closeMonkeyCages + feedTigers should have already shown alerts
            return
          }
          // handle other errors
        }
      })()
      

      【讨论】:

      • 这可以工作,但大多数 api 调用也需要一个包罗万象,因为网络错误发生了,而且它们通常不需要记录(也许我应该在我的示例中包含它)。我如何将这个包罗万象与 RangeError/TypeError/ReferenceError/etc 区分开来?
      • 通过检查错误本身?即错误的类型
      • 意思...有一个自定义错误类用于 api 调用,并在我的项目的每个 catch 块中使用 if (instanceof ApiError( { ... }
      • 不,只是最后一个 - 也许我没有像我想象的那样理解预期的程序流程
      猜你喜欢
      • 1970-01-01
      • 2019-09-14
      • 2022-08-14
      • 2021-09-30
      • 1970-01-01
      • 2018-12-15
      • 1970-01-01
      • 2021-09-19
      • 2020-11-22
      相关资源
      最近更新 更多