【问题标题】:Handle Promise Catches in Typescript处理打字稿中的承诺捕获
【发布时间】:2020-11-01 15:57:57
【问题描述】:

我有一个基于 Promise 的 API 服务,它从我的后端请求数据。它还带有自己的错误捕获,所以我不必到处写它。写成这样的时候:

BackendService.ts

...
getTasks() {
    return axios.get('/api/tasks')
        .then((response) => {
            const {
                tasks
            }: {
                tasks: tasksType
            } = response.data;
            return tasks;
        })
        .catch((error) => console.log(error));
}
...

Entries.tsx

...
const getTasks = () => {
    backendService.getTasks()
        .then((tasks: tasksType) => {
            const filteredTasksData = filterAPIDataForState(tasks);

            addTasks({
                tasks: filteredTasksData
            });
        })
}
...

我收到以下错误:

TS2345: Argument of type '(tasks: tasksType) => void'
is not assignable to parameter of type '(value: void | tasksType) => void | PromiseLike<void>'.Types of parameters 'tasks'
and 'value'
are incompatible.Type 'void | tasksType'
is not assignable to type 'tasksType'.Type 'void'
is not assignable to type 'TaskInterface[]'.

我猜这是因为 catch,这可能会使 Promise 什么也不返回(因为 console.log)。如果我从 Entries.tsxgetTasks 它自己的 catch 处理程序并将其从 BackendService.ts getTasks 中删除,它就可以工作。

如果出现错误,Typescript 是否应该能够判断 Entries.tsx 中的 .then() 不会运行,因为已经有处理这种情况的捕获?

【问题讨论】:

  • BackendService.ts 您正在返回 tasks 变量,这不是一个承诺。如果要提取数据,请使用 awaitAxios .then() 中的函数。见Promise docs。请参阅Axios example 并选择最适合您和您的项目的方法。 PS:由于您使用的是Typescript,我建议您选择await
  • @CarloCorradini 从我在Mozilla page 中看到的(见链接代码),你可以返回一个变量,下面的.then() 将能够使用它。
  • 试试this。 @Yousaf 据我所知,没有返回任何承诺:\
  • @CarloCorradini:这就是 promise 的工作方式。在 .then() 中返回的 ANYTHING 被包装在 Promise 中。类似于在用async 关键字标记的函数内返回的任何内容都会返回一个promise。因此return tasks 返回任务的承诺。请阅读 promises 和 async/await 是如何工作的
  • @Vincent 不,没有。您必须考虑getTasks() 在每次单独调用时均不返回任何内容(发生错误时)的情况,并适当地处理它(例如向用户显示错误消息)或转发错误。如果其中许多处理程序做同样的事情,您当然可以通过将相同的函数传递给每个.catch() 来共享代码。

标签: javascript typescript promise axios


【解决方案1】:

如果出现错误,Entries.tsx 中的 .then() 将不会运行,因为 已经有处理这种情况的问题了吗?

这并不完全正确。

getTasks 文件中的getTasks 块中的catch 块正在返回undefinedcatch 块返回值时 而不是:

  • 抛出一个值
  • 引发错误,或
  • 返回一个被拒绝的承诺

不是在调用代码中调用catch 块,而是调用then

这是因为backendService.ts 文件中的getTasks 方法返回的Promise 取决于以下内容:

  • 如果axios.get(...) 返回的Promise 满足,那么您在then(...) 块中执行的操作

  • 如果axios.get(...) 返回的Promise 被拒绝,那么您在catch(...) 块中执行的操作

在你的情况下,如果 axios.get(...) 返回的 Promise 满足,那么 then(...) 块将执行,因为它只返回 backendService.ts 文件中的 getTasks 方法返回的 tasksPromise满足,导致在调用代码中调用then(...)块,即在Entries.tsx文件中。

如果axios.get(...)返回的Promise被拒绝,catch(...)块将被执行。由于getTasks 方法中的catch(...) 块只是记录错误,getTasks 方法返回的Promise满足undefined 的值,这将导致调用 then(...)块在调用代码中,即在Entries.tsx文件中。

请参阅以下示例以了解这一点。

function getData() {
  // incorrect url which will lead to response.ok being false
  const url = 'https://jsonplaceholder.typicode.com/todo/1';
  return fetch(url)
          .then(response => {
            if (response.ok) {
              return response.json();
            } else {
              throw new Error();
            }
          })
          .catch(error => console.log('catch block in getData function')); 
}

getData()
  .then(data => console.log('then block ran'))
  .catch(error => console.log('error block ran'));

在上面的代码sn-p中,由于API URL不正确,then块中的response.ok为假,所以从then块中抛出错误,该块被catch块捕获相同的功能。由于这个catch 块只是记录一条消息并隐式返回undefined,所以getData 函数返回的Promise 满足undefined 的值。因此,then 块在调用 getData 函数的代码中执行,而不是 catch 块。

如果您不知道这一点,那么您可能会惊讶地看到这种行为,但这就是 Promise 与 catch 块一起工作的方式。这种行为的原因是,如果你有一个包含多个 catch 块的 Promise 链,如下所示:

fetch(...)
  .then(...)
  .catch(...)   
  .then(...)
  .then(...)
  .catch(...);

那么,如果第一个 catch 块捕获到任何链接在它之前的方法抛出的错误,那么这个 catch 块可以做以下两件事之一:

  • 抛出错误,然后将导致调用承诺链中的下一个 catch
  • 处理错误并返回一些值

如果第一个catch块正常返回,则catch块返回的promise将与catch块的返回值一起实现,然后这个值成为下一个then的回调函数的输入阻塞在 Promise 链中。因此,承诺链会继续,而不是在第一个 catch 块执行后立即停止。


回到您的代码,当catch 块在backendService.ts 文件中的getTasks 方法中执行时,它会记录消息并返回undefined,然后调用thenEntries.tsx 文件,而不是 catch 块,这就是您从 typescript 收到有关您的代码的投诉的原因。

解决方案

您可以使用以下选项之一来解决此问题:

  • backendService.ts文件中抛出getTasks方法的catch(...)块中捕获的错误,以便getTasks方法返回的Promise被拒绝,而不是用undefined的值来实现。

  • 删除backendService.ts文件中getTasks函数中的catch块,并在调用getTasks方法的代码中添加catch块。

在我看来,backendService.ts 文件中不需要catch 块,因为如果axios.get(...) 返回的Promise 被拒绝,getTasks 方法返回的Promise 也将被拒绝,如果getTasks 方法中没有 catch 块。因此,只需从 getTasks 方法中删除 catch(...) 块,然后在调用此 getTasks 方法的位置添加 catch(...) 块。

【讨论】:

  • 感谢您的回复!我实现了第二个解决方案,并将错误记录到控制台。这是我第一次看到这些多个 catch 块,但从你写的内容来看,这样做对我来说是有意义的。
【解决方案2】:

有很多方法可以解决这个问题

  1. 在 BackendService.getTasks() 中删除 .catch() 并在 Entries.getTasks() 中处理 .catch()
  2. 在 BackendService.getTasks() 中添加另一个 then 并返回空任务,例如 .then(it => it || [])

响应错误时Axios不会崩溃,所以你必须正确检查响应并处理它,因为它不应该只是盲目地破坏响应对象

【讨论】:

  • 您好,谢谢您的回答。如果我像第一个解决方案一样处理它,则每次调用 backendService.getTasks() 时都必须实现捕获。有没有办法防止这种情况?您能解释一下第二种解决方案背后的原因吗?
  • 选项2只是在出错时返回空任务。但正如我所说,Axios 在响应错误时不会崩溃,因此您必须正确检查响应并处理它,因为它不应该只是盲目地破坏响应对象。所以你一开始就不需要 .catch() 。
猜你喜欢
  • 2021-08-21
  • 2020-03-12
  • 2017-03-15
  • 1970-01-01
  • 1970-01-01
  • 2014-11-22
  • 1970-01-01
  • 2018-02-04
相关资源
最近更新 更多