处理 Typescript 中的 Promise 捕获

Vin*_*ent 6 javascript promise typescript axios

我有一个基于承诺的 API 服务,它从我的后端请求数据。它还带有自己的错误捕获,因此我不必到处编写它。当它写成这样时:

后端服务.ts

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

条目.tsx

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

            addTasks({
                tasks: filteredTasksData
            });
        })
}
...
Run Code Online (Sandbox Code Playgroud)

我收到以下错误:

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[]'.
Run Code Online (Sandbox Code Playgroud)

我想这是因为捕获,这可能会使 Promise 不返回任何内容(因为 console.log)。如果我给出getTasksfromEntries.tsx它自己的 catch 处理程序并将其从 BackendService.ts 中删除getTasks,它就会起作用。

难道 Typescript 不应该能够告诉我们,如果出现错误,则.then()in不会运行,因为已经有一个 catch 来处理这种情况?Entries.tsx

You*_*saf 6

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

这并不完全正确。

catchgetTasks文件中的方法中的块backendService.ts正在返回undefined,并且catch块返回一个值而不是:

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

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

发生这种情况是因为文件中的方法Promise返回的值取决于以下内容:getTasksbackendService.ts

  • 如果Promise返回的内容满足,那么你在块axios.get(...)中做什么then(...)

  • 如果Promise返回被axios.get(...)拒绝那么你在catch(...)块中做什么

在您的情况下,如果Promiseaxios.get(...)fulfils 返回,则then(...)块将执行,并且因为它只返回由文件中的方法返回的 fulfils tasks,导致在调用代码中调用该块,即在文件中。PromisegetTasksbackendService.tsthen(...)Entries.tsx

如果Promise返回的axios.get(...)被拒绝,catch(...)则块将执行。由于方法catch(...)中的块getTasks只是记录错误,因此方法Promise返回的值将导致在调用代码(即文件中)中调用块。getTasksundefinedthen(...)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'));
Run Code Online (Sandbox Code Playgroud)

在上面的代码片段中,由于 API URL 不正确,response.ok块中的then值为 false,因此从块中抛出错误,该错误被同一函数中的块then捕获。catch由于此catch块只是记录一条消息并隐式返回undefinedPromise因此函数getData返回的值为undefined。因此catchthen块不是在块中执行,而是在调用该getData函数的代码中执行。

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

fetch(...)
  .then(...)
  .catch(...)   
  .then(...)
  .then(...)
  .catch(...);
Run Code Online (Sandbox Code Playgroud)

那么如果第一个catch块捕获了从它之前链接的任何方法引发的错误,则该catch块可以执行以下两件事之一:

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

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


回到你的代码,当块在文件中的方法catch中执行时,它会记录消息并返回,然后导致调用文件中的块,而不是块,这就是为什么你会收到打字稿的投诉你的代码。getTasksbackendService.tsundefinedthenEntries.tsxcatch

解决方案

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

  • 将方法catch(...)块中捕获的错误抛出到文件中,以便方法返回被拒绝,而不是用 的值来实现。getTasksbackendService.tsPromisegetTasksundefined

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

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


Wor*_*bat 0

有很多选项可以处理这个问题

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

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