Обработка перехвата обещаний в машинописном тексте - PullRequest
2 голосов
/ 12 июля 2020

У меня есть служба 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[]'.

Я думаю, это из-за ловушки, которая может заставить Promise ничего не вернуть (из-за console.log). Если я передам getTasks из Entries.tsx его собственный обработчик catch и удалю его из BackendService.ts getTasks, он заработает.

Разве Typescript не сможет сказать, что .then() в Entries.tsx не запустился бы, если бы произошла ошибка, поскольку в этой ситуации уже есть уловка?

Ответы [ 2 ]

2 голосов
/ 12 июля 2020

.then () в Entries.tsx не запустился бы, если бы произошла ошибка, поскольку в этой ситуации уже есть перехватчик?

Это не совсем правильно.

catch блок в методе getTasks в backendService.ts файле возвращает undefined и , когда блок catch возвращает значение вместо того, чтобы генерировать перехваченную ошибку, вместо вызова catch блок вызывающего кода, then блок вызывается .

Это происходит потому, что Promise, возвращаемый методом getTasks в файле backendService.ts, зависит от следующего:

  • Если Promise, возвращаемый axios.get(...), соответствует тому, что вы делаете в блоке then(...)

  • Если Promise вернул by axios.get(...) отклоняется, тогда то, что вы делаете в блоке catch(...)

В вашем случае, если Promise, возвращенный axios.get(...), выполняется, тогда блок then(...) будет execute, и поскольку он просто возвращает tasks, Promise, возвращенный методом getTasks в backendService.ts файле, выполняется, что приводит к вызову then(...) блок в вызывающем коде, то есть в файле Entries.tsx.

Если Promise, возвращенный axios.get(...), отклоняется, блок catch(...) будет выполнен. Поскольку блок catch(...) в методе getTasks просто регистрирует ошибку, Promise, возвращаемый методом getTasks, будет выполнен со значением 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'));

В приведенном выше фрагменте кода, поскольку URL-адрес API неверен, response.ok в блоке then является ложным, поэтому ошибка возникает из блока then который перехватывается блоком catch в той же функции. Поскольку этот блок catch просто регистрирует сообщение и возвращает undefined, Promise, возвращаемый функцией getData, выполняет со значением undefined. Таким образом, вместо блока catch блок then выполняется в коде, который вызывает функцию getData.

Если вы этого не знали, вы можете быть удивлены, увидев такое поведение, но вот как Promises работает с catch блоками. Причина такого поведения в том, что если у вас есть цепочка обещаний, содержащая более одного блока catch, как показано ниже:

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

, тогда, если первый блок catch улавливает ошибку, которая выдается от любой из функций, связанных перед ним, то этот блок catch может выполнять одно из следующих двух действий:

  • выдавать ошибку, которая затем приведет к вызову последнего блока catch
  • обрабатывает ошибку и возвращает некоторое значение

, если первый блок catch возвращается нормально, обещание, возвращаемое блоком catch, выполняется с этим возвращаемым значением catch block, и это значение затем становится входом функции обратного вызова следующего блока then в цепочке обещаний. Таким образом, цепочка обещаний продолжается, а не останавливается, как только выполняется первый catch блок.

Возвращаясь к вашему коду, когда блок catch выполняется в методе getTasks в файле backendService.ts, он регистрирует сообщение и возвращает undefined, что затем приводит к вызову блока then в Entries.tsx вместо блока catch, поэтому вы получаете жалобу от машинописного текста на ваш код.

Решение

Вы можете использовать один из следующие варианты решения этой проблемы:

  • Вывести ошибку, обнаруженную в блоке catch(...) метода getTasks в файле backendService.ts, чтобы Promise возвращалось методом getTasks отклоняется вместо выполнения со значением undefined.

  • Удалите блок catch в функции getTasks в файле backendService.ts и добавьте блок catch в код, вызывающий метод getTasks.

На мой взгляд, нет необходимости в блоке catch в файле backendService.ts, потому что, если Promise, возвращенный axios.get(...), отклоняется, Promise, возвращенный методом getTasks, также будет отклонен, если в методе getTasks нет блока catch. Поэтому просто удалите блок catch(...) из метода getTasks и добавьте блок catch(...), в котором вы вызываете этот метод getTasks.

0 голосов
/ 12 июля 2020

Есть много способов справиться с этим

  1. В BackendService.getTasks () просто удалите .catch () и обработайте .catch () в Entries.getTasks ()
  2. В BackendService.getTasks () добавьте еще один затем и вернитесь с пустыми задачами, например .then (it => it || [])

Ax ios не треснет sh при ответе ошибка, поэтому вы должны правильно проверить ответ и обработать его так, как должно быть, а не просто слепо уничтожить объект ответа

...