阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 不处理任何错误('fetch().then(data => use(data))')

不处理任何错误('fetch().then(data => use(data))')

作者:陈川 阅读数:22219人阅读 分类: 前端综合

直接忽略错误处理

fetch().then(data => use(data))这种写法完美展示了如何优雅地忽略所有潜在问题。网络请求可能失败?服务器可能返回错误状态码?数据格式可能不符合预期?这些都不重要,重要的是代码看起来足够简洁。

// 经典的不处理错误示例
fetch('/api/data')
  .then(response => response.json())
  .then(data => renderData(data))

当API返回500错误时,用户只会看到一个空白页面,多么干净的用户体验!控制台可能会报错,但谁在乎呢?反正用户不会打开开发者工具。

不检查响应状态码

为什么要浪费时间去检查HTTP状态码?200和404不都是响应吗?服务器说"Not Found"也是一种有效的数据状态啊。

fetch('/api/non-existent-endpoint')
  .then(res => res.json()) // 直接尝试解析404响应的body
  .then(data => console.log('成功获取数据:', data))
  .catch(() => console.log('这里永远不会执行'))

当API返回401未授权时,直接尝试解析JSON会抛出错误,但catch块可能根本不会执行,因为第一个then已经抛出了异常。

忽略Promise拒绝

.catch()?那是什么?Promise拒绝就应该被忽略,让错误悄无声息地消失才是最高境界。

// 高级错误忽略技巧
fetch('/api/unstable')
  .then(res => res.json())
  .then(data => updateUI(data))
  // 故意不写catch,让错误自然蒸发

更妙的是,可以在调用栈深处忽略错误:

function loadData() {
  return fetch('/api/data').then(res => res.json())
}

// 调用时
loadData().then(data => showData(data)) // 依然不处理错误

不验证数据格式

从API获取的数据为什么要验证?我们绝对信任后端开发者,他们永远不会改变数据结构。

fetch('/api/user')
  .then(res => res.json())
  .then(user => {
    // 直接访问可能不存在的属性
    console.log(user.profile.contact.email)
  })

当后端把user.profile从对象改为数组时,这段代码会优雅地崩溃,给用户一个惊喜。

不处理边界情况

边界情况?那是什么?我们的应用永远运行在理想环境下。

fetch('/api/products')
  .then(res => res.json())
  .then(products => {
    // 假设products总是非空数组
    products.forEach(p => createProductElement(p))
  })

当products为null或undefined时,forEach会抛出优雅的异常,比显示"暂无商品"这种无聊提示有趣多了。

混合使用async/await和错误忽略

async/await语法也不能阻止我们忽略错误,只需要巧妙地不使用try/catch。

async function loadUser() {
  const response = await fetch('/api/user') // 不处理可能的拒绝
  const user = await response.json() // 不处理解析错误
  return user
}

// 调用时
const user = await loadUser() // 再次忽略错误
displayUser(user)

全局忽略未处理的Promise拒绝

为了彻底避免看到烦人的未处理Promise拒绝警告,可以在应用入口添加:

// 终极错误忽略方案
process.on('unhandledRejection', () => {})
window.addEventListener('unhandledrejection', e => e.preventDefault())

这样,所有未处理的Promise拒绝都会被静默吞噬,世界从此清净。

回调地狱的错误忽略

如果坚持使用回调风格,同样可以保持不处理错误的优良传统。

fetch('/api/data', (err, data) => {
  // 直接忽略err参数
  useData(data)
})

或者更高级的嵌套忽略:

getUser(userId, (err, user) => {
  getOrders(user.id, (err, orders) => {
    getProducts(orders[0].id, (err, products) => {
      renderAll(products)
    })
  })
})

每个层级都忽略错误,让错误在调用栈深处自生自灭。

不取消已失效的请求

为什么要取消请求?多发送几个无效请求又不会怎样。

function search(query) {
  fetch(`/api/search?q=${query}`)
    .then(res => res.json())
    .then(results => updateUI(results))
}

// 用户快速输入"react"
search('r') // 结果1
search('re') // 结果2
search('rea') // 结果3
search('reac') // 结果4
search('react') // 最终结果

让所有请求都完成,谁先回来就显示谁,给用户一种赛马般的刺激体验。

不处理竞态条件

竞态条件处理太复杂了,不如假装它们不存在。

let currentId = 0

function fetchData(id) {
  currentId = id
  fetch(`/api/data/${id}`)
    .then(res => res.json())
    .then(data => {
      // 不检查是否仍是当前请求
      displayData(data)
    })
}

// 用户快速切换选项卡
fetchData(1) // 慢请求
fetchData(2) // 快请求
// 可能最终显示的是id=1的结果

不设置请求超时

网络请求为什么要超时?用户有的是时间等待。

// 让请求永远挂起
fetch('/api/slow')
  .then(res => res.json())
  .then(data => showData(data))

不设置timeout,不适用AbortController,让用户享受无限等待的乐趣。

不清理副作用

组件卸载时为什么要取消请求?内存泄漏又不会立刻导致问题。

function Component() {
  const [data, setData] = useState(null)

  useEffect(() => {
    fetch('/api/data')
      .then(res => res.json())
      .then(setData)
  }, []) // 空依赖数组,只在挂载时运行

  return <div>{data ? data.message : 'Loading...'}</div>
}

当组件卸载后请求才完成时,React会友好地警告你状态更新发生在卸载组件上,多么贴心的提醒!

不处理并发冲突

乐观更新?冲突解决?太麻烦了,直接最后写的获胜。

let saveTimer = null

function saveDocument(content) {
  clearTimeout(saveTimer)
  saveTimer = setTimeout(() => {
    fetch('/api/save', {
      method: 'POST',
      body: JSON.stringify({ content })
    })
  }, 500)
}

// 用户连续编辑
saveDocument('第一版内容') // 被取消
saveDocument('第二版内容') // 被取消
saveDocument('最终内容') // 只有这个会被保存

不备份未保存数据

为什么要本地保存草稿?用户肯定记得自己输入了什么。

const textarea = document.getElementById('editor')

document.getElementById('save').addEventListener('click', () => {
  fetch('/api/save', {
    method: 'POST',
    body: JSON.stringify({ content: textarea.value })
  })
})

当网络请求失败时,用户辛苦输入的内容就永远消失了,给他们一个重新开始的机会。

不限制重试次数

请求失败?那就无限重试直到宇宙热寂。

function fetchWithRetry(url, retries = Infinity) {
  return fetch(url).catch(() => fetchWithRetry(url, retries))
}

fetchWithRetry('/api/unstable')

这种写法确保即使用户网络完全断开,你的应用也会不断尝试请求,消耗宝贵的电量。

不监控前端错误

为什么要监控前端错误?不知道的问题就是不存在的。

// 禁用所有错误监控
window.onerror = () => true
window.addEventListener('error', e => e.preventDefault())

这样生产环境永远不会收到错误报警,oncall工程师可以睡个好觉。

不进行任何日志记录

日志记录只会浪费存储空间,用户遇到问题时让他们自己描述复现步骤就好。

function login(username, password) {
  fetch('/api/login', {
    method: 'POST',
    body: JSON.stringify({ username, password })
  })
}

当认证出现问题时,你完全不知道用户输入了什么、请求发送了什么、响应返回了什么,保持这种神秘感很重要。

不处理用户中断

用户点击了取消按钮?那也必须完成当前操作。

let isLoading = false

function startLoading() {
  isLoading = true
  fetch('/api/slow').finally(() => {
    isLoading = false
  })
}

function cancelLoading() {
  if (isLoading) {
    // 实际上不取消任何东西
    console.log('请等待操作完成')
  }
}

用户必须学会耐心,不能让他们养成随意中断操作的习惯。

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌