简述网站规划的主要内容,建筑网站官网,wordpress插件都是英文,张店网络推广公司取消功能的设计与实现 #需求分析 有些场景下#xff0c;我们希望能主动取消请求#xff0c;比如常见的搜索框案例#xff0c;在用户输入过程中#xff0c;搜索框的内容也在不断变化#xff0c;正常情况每次变化我们都应该向服务端发送一次请求。但是当用户输入过快的时候我们希望能主动取消请求比如常见的搜索框案例在用户输入过程中搜索框的内容也在不断变化正常情况每次变化我们都应该向服务端发送一次请求。但是当用户输入过快的时候我们不希望每次变化请求都发出去通常一个解决方案是前端用 debounce 的方案比如延时 200ms 发送请求。这样当用户连续输入的字符只要输入间隔小于 200ms前面输入的字符都不会发请求。 但是还有一种极端情况是后端接口很慢比如超过 1s 才能响应这个时候即使做了 200ms 的 debounce但是在我慢慢输入每个输入间隔超过 200ms的情况下在前面的请求没有响应前也有可能发出去多个请求。因为接口的响应时长是不定的如果先发出去的请求响应时长比后发出去的请求要久一些后请求的响应先回来先请求的响应后回来就会出现前面请求响应结果覆盖后面请求响应结果的情况那么就乱了。因此在这个场景下我们除了做 debounce还希望后面的请求发出去的时候如果前面的请求还没有响应我们可以把前面的请求取消。 从 axios 的取消接口设计层面我们希望做如下的设计 const CancelToken axios.CancelToken;
const source CancelToken.source(); axios.get(/user/12345, { cancelToken: source.token }).catch(function (e) { if (axios.isCancel(e)) { console.log(Request canceled, e.message); } else { // 处理错误 } }); // 取消请求 (请求原因是可选的) source.cancel(Operation canceled by the user.); 我们给 axios 添加一个 CancelToken 的对象它有一个 source 方法可以返回一个 source 对象source.token 是在每次请求的时候传给配置对象中的 cancelToken 属性然后在请求发出去之后我们可以通过 source.cancel 方法取消请求。 我们还支持另一种方式的调用 const CancelToken axios.CancelToken;
let cancel; axios.get(/user/12345, { cancelToken: new CancelToken(function executor(c) { cancel c; }) }); // 取消请求 cancel(); axios.CancelToken 是一个类我们直接把它实例化的对象传给请求配置中的 cancelToken 属性CancelToken 的构造函数参数支持传入一个 executor 方法该方法的参数是一个取消函数 c我们可以在 executor 方法执行的内部拿到这个取消函数 c赋值给我们外部定义的 cancel 变量之后我们可以通过调用这个 cancel 方法来取消请求。 #异步分离的设计方案 通过需求分析我们知道想要实现取消某次请求我们需要为该请求配置一个 cancelToken然后在外部调用一个 cancel 方法。 请求的发送是一个异步过程最终会执行 xhr.send 方法xhr 对象提供了 abort 方法可以把请求取消。因为我们在外部是碰不到 xhr 对象的所以我们想在执行 cancel 的时候去执行 xhr.abort 方法。 现在就相当于我们在 xhr 异步请求过程中插入一段代码当我们在外部执行 cancel 函数的时候会驱动这段代码的执行然后执行 xhr.abort 方法取消请求。 我们可以利用 Promise 实现异步分离也就是在 cancelToken 中保存一个 pending 状态的 Promise 对象然后当我们执行 cancel 方法的时候能够访问到这个 Promise 对象把它从 pending 状态变成 resolved 状态这样我们就可以在 then 函数中去实现取消请求的逻辑类似如下的代码
if (cancelToken) {cancelToken.promise .then(reason { request.abort() reject(reason) }) } #CancelToken 类实现 接下来我们就来实现这个 CancelToken 类先来看一下接口定义 #接口定义 types/index.ts export interface AxiosRequestConfig {// ... cancelToken?: CancelToken } export interface CancelToken { promise: Promisestring reason?: string } export interface Canceler { (message?: string): void } export interface CancelExecutor { (cancel: Canceler): void } 其中 CancelToken 是实例类型的接口定义Canceler 是取消方法的接口定义CancelExecutor 是 CancelToken 类构造函数参数的接口定义。 #代码实现 我们单独创建 cancel 目录来管理取消相关的代码在 cancel 目录下创建 CancelToken.ts 文件 import { CancelExecutor } from ../types interface ResolvePromise { (reason?: string): void } export default class CancelToken { promise: Promisestring reason?: string constructor(executor: CancelExecutor) { let resolvePromise: ResolvePromise this.promise new Promisestring(resolve { resolvePromise resolve }) executor(message { if (this.reason) { return } this.reason message resolvePromise(this.reason) }) } } 在 CancelToken 构造函数内部实例化一个 pending 状态的 Promise 对象然后用一个 resolvePromise 变量指向 resolve 函数。接着执行 executor 函数传入一个 cancel 函数在 cancel 函数内部会调用 resolvePromise 把 Promise 对象从 pending 状态变为 resolved 状态。 接着我们在 xhr.ts 中插入一段取消请求的逻辑。 core/xhr.ts const { /*....*/ cancelToken } config if (cancelToken) { cancelToken.promise.then(reason { request.abort() reject(reason) }) } 这样就满足了第二种使用方式接着我们要实现第一种使用方式给 CancelToken 扩展静态接口。 #CancelToken 扩展静态接口 #接口定义 types/index.ts export interface CancelTokenSource {token: CancelToken cancel: Canceler } export interface CancelTokenStatic { new(executor: CancelExecutor): CancelToken source(): CancelTokenSource } 其中 CancelTokenSource 作为 CancelToken 类静态方法 source 函数的返回值类型CancelTokenStatic 则作为 CancelToken 类的类类型。 #代码实现 cancel/CancelToken.ts export default class CancelToken { // ... static source(): CancelTokenSource { let cancel!: Canceler const token new CancelToken(c { cancel c }) return { cancel, token } } } source 的静态方法很简单定义一个 cancel 变量实例化一个 CancelToken 类型的对象然后在 executor 函数中把 cancel 指向参数 c 这个取消函数。 这样就满足了我们第一种使用方式但是在第一种使用方式的例子中我们在捕获请求的时候通过 axios.isCancel 来判断这个错误参数 e 是不是一次取消请求导致的错误接下来我们对取消错误的原因做一层包装并且把给 axios 扩展静态方法 #Cancel 类实现及 axios 的扩展 #接口定义 export interface Cancel {message?: string } export interface CancelStatic { new(message?: string): Cancel } export interface AxiosStatic extends AxiosInstance { create(config?: AxiosRequestConfig): AxiosInstance CancelToken: CancelTokenStatic Cancel: CancelStatic isCancel: (value: any) boolean } 其中 Cancel 是实例类型的接口定义CancelStatic 是类类型的接口定义并且我们给 axios 扩展了多个静态方法。 #代码实现 我在 cancel 目录下创建 Cancel.ts 文件。 export default class Cancel { message?: string constructor(message?: string) { this.message message } } export function isCancel(value: any): boolean { return value instanceof Cancel } Cancel 类非常简单拥有一个 message 的公共属性。isCancel 方法也非常简单通过 instanceof来判断传入的值是不是一个 Cancel 对象。 接着我们对 CancelToken 类中的 reason 类型做修改把它变成一个 Cancel 类型的实例。 先修改定义部分。 types/index.ts export interface CancelToken {promise: PromiseCancel reason?: Cancel } 再修改实现部分 import Cancel from ./Cancelinterface ResolvePromise { (reason?: Cancel): void } export default class CancelToken { promise: PromiseCancel reason?: Cancel constructor(executor: CancelExecutor) { let resolvePromise: ResolvePromise this.promise new PromiseCancel(resolve { resolvePromise resolve }) executor(message { if (this.reason) { return } this.reason new Cancel(message) resolvePromise(this.reason) }) } } 接下来我们给 axios 扩展一些静态方法供用户使用。 axios.ts import CancelToken from ./cancel/CancelToken
import Cancel, { isCancel } from ./cancel/Cancel axios.CancelToken CancelToken axios.Cancel Cancel axios.isCancel isCancel #额外逻辑实现 除此之外我们还需要实现一些额外逻辑比如当一个请求携带的 cancelToken 已经被使用过那么我们甚至都可以不发送这个请求只需要抛一个异常即可并且抛异常的信息就是我们取消的原因所以我们需要给 CancelToken 扩展一个方法。 先修改定义部分。 types/index.ts export interface CancelToken {promise: PromiseCancel reason?: Cancel throwIfRequested(): void } 添加一个 throwIfRequested 方法接下来实现它 cancel/CancelToken.ts export default class CancelToken { // ... throwIfRequested(): void { if (this.reason) { throw this.reason } } } 判断如果存在 this.reason说明这个 token 已经被使用过了直接抛错。 接下来在发送请求前增加一段逻辑。 core/dispatchRequest.ts export default function dispatchRequest(config: AxiosRequestConfig): AxiosPromise { throwIfCancellationRequested(config) processConfig(config) // ... } function throwIfCancellationRequested(config: AxiosRequestConfig): void { if (config.cancelToken) { config.cancelToken.throwIfRequested() } } 发送请求前检查一下配置的 cancelToken 是否已经使用过了如果已经被用过则不用法请求直接抛异常。 #demo 编写 在 examples 目录下创建 cancel 目录在 cancel 目录下创建 index.html: !DOCTYPE html
html langen head meta charsetutf-8 titleCancel example/title /head body script src/__build__/cancel.js/script /body /html 接着创建 app.ts 作为入口文件 import axios, { Canceler } from ../../src/index const CancelToken axios.CancelToken const source CancelToken.source() axios.get(/cancel/get, { cancelToken: source.token }).catch(function(e) { if (axios.isCancel(e)) { console.log(Request canceled, e.message) } }) setTimeout(() { source.cancel(Operation canceled by the user.) axios.post(/cancel/post, { a: 1 }, { cancelToken: source.token }).catch(function(e) { if (axios.isCancel(e)) { console.log(e.message) } }) }, 100) let cancel: Canceler axios.get(/cancel/get, { cancelToken: new CancelToken(c { cancel c }) }).catch(function(e) { if (axios.isCancel(e)) { console.log(Request canceled) } }) setTimeout(() { cancel() }, 200) 我们的 demo 展示了 2 种使用方式也演示了如果一个 token 已经被使用过则再次携带该 token 的请求并不会发送。 至此我们完成了 ts-axios 的请求取消功能我们巧妙地利用了 Promise 实现了异步分离。目前官方 axios 库的一些大的 feature 我们都已经实现了下面的章节我们就开始补充完善 ts-axios 的其它功能。 转载于:https://www.cnblogs.com/QianDingwei/p/11403916.html