Мало знаний, большой вызов! Эта статья участвует в "Необходимые знания для программистов«Творческая деятельность.
предисловие
Недавно маленький партнер по проекту столкнулся с очень странным багом, после входа на страницу быстро переключаешься на другие страницы, а он перескакивает на страницу 403. После периода расследования с небольшим партнером было обнаружено, что на странице был запрос интерфейса, на который требовалось много времени, и после запроса произошла некоторая бизнес-обработка. Когда мы переключаемся на другие страницы, оставшиеся дела будут обработаны после завершения запроса, что приведет к ошибке.
Кодовый регистр
В проекте много предприятий, и мы воспроизводим эту проблему с помощью простого кода.
import React, { useEffect } from 'react';
import { history } from 'umi';
const Test = props => {
useEffect(() => {
new Promise((resolve, reject) => {
// 模拟接口请求时间
setTimeout(() => {
resolve()
}, 4000);
}).then(res => {
return new Promise((resolve1) => {
// 模拟接口请求时间
setTimeout(() => {
resolve1()
}, 1000);
})
}).then(() => {
// Promise 执行完后页面跳转
history.push('/test1')
})
}, []);
const go = () => {
history.push('/user/login')
}
return (
<div>
<button onClick={go}>go to</button>
Test
</div>
);
}
export default Test;
мы входимTestПосле компонента нажмитеgo toКнопка, через несколько секунд страница перейдет кtest1страница.
После анализа, мы должны спросить, когда мы уходим取消请求а также取消PromiseОтменить запрос относительно просто, если последующий бизнес-код не выполняется, а общая библиотека это поддерживает.Поговорим о том, как отменить промис.
CancelablePromise (отменить обещание)
мы знаемPromiseДействия по отмене или прекращению действия не предусмотрены. Но мы столкнемся с этим в процессе разработки. Мы можем ссылаться и использоватьAbortControllerреализовать.
class CancelablePromise<T> {
/**
* 构造器
* @param executor Promise中的 executor
* @param abortSignal AbortController中的signal对象
* @returns
*/
constructor(executor: (resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void, abortSignal: AbortSignal) {
// 记录reject和resolve方法
let _reject: any = null;
let _resolve: any = null;
let _isExecResolve = false;
// 创建和执行Promise
const cancelablePromise = new Promise<T>((resolve, reject) => {
_reject = reject;
_resolve = (value: T) => {
_isExecResolve = true;
resolve(value);
};
return executor(_resolve, reject);
});
// 监听Signal的abourt事件
abortSignal.addEventListener('abort', () => {
if (_isExecResolve) {
return;
}
// 抛出错误
const error = new DOMException('user cancel promise', CancelablePromise.CancelExceptionName );
_reject( error );
} );
return cancelablePromise;
}
// 取消后抛出的异常名称
static CancelExceptionName = 'CancelablePromise AbortError';
}
export default CancelablePromise;
использовать
Теперь мы должны собрать предыдущий код и использовать наш инкапсулированныйCancelablePromise, может позволитьPromiseОтменяемый.
Объединение нескольких промисов
import React, { useEffect } from 'react';
import { history } from 'umi';
import CancelablePromise from '../utils/CancelablePromise';
const Test = props => {
useEffect(() => {
let abortController = new AbortController();
new CancelablePromise((resolve, reject) => {
// 模拟接口请求时间
setTimeout(() => {
resolve()
}, 4000);
}, abortController.signal).then(res => {
return new CancelablePromise((resolve1) => {
// 模拟接口请求时间
setTimeout(() => {
resolve1()
}, 1000);
}, abortController.signal)
}).then(() => {
// Promise 执行完后页面跳转
history.push('/test1')
})
return () => {
// 取消请求
abortController.abort();
}
}, []);
const go = () => {
history.push('/user/login')
}
return (
<div>
<button onClick={go}>go to</button>
Test
</div>
);
}
export default Test;
Использовать в асинхронном режиме и ждать
import React, { useEffect } from 'react';
import { history } from 'umi';
import CancelablePromise from '../utils/CancelablePromise';
const Test = props => {
useEffect(() => {
let abortController = new AbortController();
const exec = async function () {
try {
await new CancelablePromise((resolve) => {
setTimeout(() => {resolve();}, 5000);
}, abortController.signal, 'one')
await new CancelablePromise((resolve1) => {
setTimeout(() => {resolve1();}, 5000);
}, abortController.signal, 'del')
history.push('/test')
} catch (error) {
// 取消之后会抛出异常
if (CancelablePromise.CancelExceptionName === error.name) {
console.log('promise 终止了。。。')
}
}
}
exec();
return () => {
// 取消请求
abortController.abort();
}
}, []);
}
PromiseПосле отмены будет создано исключение. Если вам нужно обработать исключение после того, как оно было создано, вы можете передать соответствующее имя исключения для суждения.
if (CancelablePromise.CancelExceptionName === error.name) {
console.log('promise 终止了。。。')
}
заключительные замечания
PromiseОтмена используется во многих местах в бизнесе, у всех есть другие хорошие способы, добро пожаловать на обмен в области комментариев.
Если вы считаете, что эта статья хороша, вы можете также
1,подобно, чтобы больше людей могли увидеть этот контент
2,Подписывайтесь на меня, пусть будут долгосрочные отношения
3. Подпишитесь на официальный аккаунт »Фронтенду есть что сказать», в ней много оригинальных статей и средств разработки, приветствую ваше внимание, и читайте мои статьи как можно скорее