Интервьюер: Как прервать исходящий запрос?

внешний интерфейс HTTP
Интервьюер: Как прервать исходящий запрос?

«Это 13-й день моего участия в ноябрьском испытании обновлений. Подробную информацию об этом событии см.:Вызов последнего обновления 2021 г."

Интервьюер: Запрос отправлен, как отменить отправленный запрос?

Опрашиваемый: (Сразу возникает сомнение: можно ли отменить уже отправленный запрос?) Это... это... я действительно не знаю.

После интервью я поищу Ду Ньянга...  

Рекомендуемое чтение:Принцип отмены запроса cancelToken анализа аксиом

AbortController

AbortControllerИнтерфейс представляет собой объект контроллера, который может завершать один или несколько веб-запросов по мере необходимости.

  • AbortController(): Конструктор AbortController() создает новый экземпляр объекта AbortController.

  • signalСвойство :signal возвращает экземпляр объекта AbortSignal, который можно использовать с/о веб-запросе (сети).

  • abort(): Завершает ожидающий веб-запрос (сетевой), он может завершить запрос на выборку, любые потребители и потоки, которые отвечают на Body

Запрос на прерывание

Fetch - это интерфейс, предоставляемый веб-интерфейсом для получения ресурсов. Если вы хотите завершить запрос Fetch, вы можете использовать интерфейс AbortController, предоставленный Web.

Сначала мы создаем контроллер с помощью конструктора AbortController(), затем используем свойство AbortController.signal, чтобы получить ссылку на связанный с ним объект AbortSignal. Когда инициализируется запрос на выборку, мы передаем AbortSignal в качестве опции объекту запроса (следующим образом: {signal}). Это связывает сигнал и контроллер с запросом на получение, а затем позволяет нам прервать запрос, вызвав AbortController.abort().

const controller = new AbortController();
let signal = controller.signal;
 console.log('signal 的初始状态: ', signal);

const downloadBtn = document.querySelector('.download');
const abortBtn = document.querySelector('.abort');

downloadBtn.addEventListener('click', fetchVideo);

abortBtn.addEventListener('click', function() {
  controller.abort();
 console.log('signal 的中止状态: ', signal);
});

function fetchVideo() {
  //...
  fetch(url, {signal}).then(function(response) {
    //...
  }).catch(function(e) {
    reports.textContent = 'Download error: ' + e.message;
  })
}

Когда мы прерываем запрос, сетевой запрос становится примерно таким:

Давайте посмотрим на состояние AbortSignal до и после прерывания:

Как видите, свойство aborted объекта AbortSignal изначально изменилось с false на true после прерывания.

Пример онлайн-запуска(код изMDN)

AbortControllter имеет следующие проблемы совместимости:

запрос прерывания аксиомы

Существует два способа запроса прерывания аксионов:

метод первый

Используйте фабричный метод CancelToken.souce, чтобы создать токен отмены со следующим кодом:

const CancelToken = axios.CancelToken;
const source = CancelToken.source();

axios.get('https://mdn.github.io/dom-examples/abort-api/sintel.mp4', {
  cancelToken: source.token
}).catch(function (thrown) {
  // 判断请求是否已中止
  if (axios.isCancel(thrown)) {
    // 参数 thrown 是自定义的信息
    console.log('Request canceled', thrown.message);
  } else {
    // 处理错误
  }
});

// 取消请求(message 参数是可选的)
source.cancel('Operation canceled by the user.');

Прерванный сетевой запрос становится следующим:

Давайте посмотрим на начальное и прерванное состояние источника:

Видно, что исходное состояние не изменилось в начале и после прерывания. Итак, как мы можем судить о прерванном статусе запроса? axios предоставляет нам метод isCancel() для определения прерванного статуса запроса. Параметр метода isCancel() — это информация, которую мы настраиваем при прерывании запроса.

Способ 2

Создайте токен отмены, передав функцию-исполнитель конструктору CancelToken:

const CancelToken = axios.CancelToken;
let cancel;

axios.get('/user/12345', {
  cancelToken: new CancelToken(function executor(c) {
    // executor 函数接收一个 cancel 函数作为参数
    cancel = c;
  })
});

// 取消请求
cancel('Operation canceled by the user.');

Результаты браузера такие же, какметод первыйПоследовательны и не будут повторяться здесь.

Пример онлайн-запуска(код изMDN)

umi-запрос на прерывание

umi-запрос основан на инкапсуляции выборки и имеет характеристики выборки и аксиом. Запрос на прерывание согласуется с выборкой и аксиомами и не будет слишком часто повторяться. Подробности см. в официальном документе.прервать запрос

Следует отметить, что существует проблема с полифиллом AbortController в браузерах более ранних версий, а umi-request не предоставляет возможности прервать запрос в некоторых версиях AbortController.

CancelToken используется для отмены запроса в проекте umi.

Библиотекой запросов по умолчанию в проекте umi является umi-request, поэтому мы можем использовать методы, предоставляемые umi-request, чтобы прервать запрос. Кроме того, dva можно использовать совместно с проектом umi, поэтому ниже приводится краткое введение в процесс использования CancelToken для прерывания запроса в dva.

1, напишите функцию запроса на отмену запроса и функцию файла в службах каталогов.

import request from '@/utils/request';
const CancelToken = request.CancelToken;
let cancel: any;

// 合同文件上传 OSS
export async function uploadContractFileToOSS(postBody: Blob): Promise<any> {
  return request(`/fms/ossUpload/financial_sys/contractFile`, {
    method: "POST",
    data: postBody,
    requestType: 'form',
    // 传递一个 executor 函数到 CancelToken 的构造函数来创建一个 cancel token
    cancelToken: new CancelToken((c) => {
      cancel = c
    })
  })
}

// 取消合同文件上传
export async function cancelUploadFile() {
  return cancel && cancel()
}

2. Напишите Эффект в моделях:

*uploadContractFileToOSS({ payload }: AnyAction, { call, put }: EffectsCommandMap): any {
  const response = yield call(uploadContractFileToOSS, payload);
  yield put({
    type: 'save',
    payload: {
      uploadOSSResult: response?.data,
    }
  })
  return response?.data
},

*cancelUploadFile(_: AnyAction, { call }: EffectsCommandMap): any {
  const response = yield call(cancelUploadFile)
  return response

},

3. Запустите соответствующее действие через функцию отправки на странице:

// 发起请求
dispatch({
  type: 'contract/fetchContractFiles',
  payload: {
    contractId: `${id}`,
  }
})

// 取消请求
dispatch({
  type: "contract/cancelUploadFile"
})
   

4. Унифицирована обработка перехвата прерванных запросов в utils/request.js:

const errorHandler = (error: { response: Response }): Response => {
  const { response } = error;
  notification.destroy()

  if (response && response.status) {
    const errorText = codeMessage[response.status] || response.statusText;
    const { status, url } = response;

    notification.error({
      message: `请求错误 ${status}: ${url}`,
      description: errorText,
    });
  } else if (error?.['type'] === 'TypeError') {
    notification.error({
      description: '您的网络发生异常,无法连接服务器',
      message: '网络异常',
    });
  } else if (error?.['request']?.['options']?.['cancelToken']) {
    notification.warn({
      description: '当前请求已被取消',
      message: '取消请求',
    });
  } else if (!response) {
    notification.error({
      description: '您的网络发生异常,无法连接服务器',
      message: '网络异常',
    });
  } else {
    notification.error({
      description: '请联系网站开发人员处理',
      message: '未知错误',
    });
  }
  return response;
};