【笔试题解】http-client-module

实现方案:

http-client-module.ts时序不太理解为啥这么干,没有实现

export interface HttpClientProps {
 concurrency: number;
 request: (url: string, init?: RequestInit) => [fetchReq: Promise<any>, abort: () => void];
 keyRender: (url: string, init?: RequestInit) => string;
}
export interface FetchQueueItem {
 key: string;
 url: string;
 init: RequestInit & { abortHandler?: HttpAbortHandler }
 resolve: (res: any) => void;
 reject: (err: any) => void;
 status: 'init' | 'fetch' | 'done' | 'error';
}
export class HttpClient {
 private props = defaultClientProps
 constructor (props?: Partial<HttpClientProps>) {
 if (props) {
 this.props = Object.assign(defaultClientProps, props)
 }
 }
 private queue: FetchQueueItem[] = []
 private __do_fetch__ = () => {
 const { queue, props: { concurrency, request }, __do_fetch__ } = this
 let fetching = 0
 let first_init_item: FetchQueueItem | null = null
 for (let i = 0; i < queue.length; i++) {
 const item = queue[i];
 switch (item.status) {
 case 'init':
 first_init_item = first_init_item || item
 break;
 case 'fetch':
 fetching++;
 break;
 }
 }
 if (fetching < concurrency && first_init_item) {
 (function (item) {
 const { url, init, resolve, reject } = item
 item.status = 'fetch'
 const [ fetchReq, abort ] = request(url, init)
 init.abortHandler && init.abortHandler.setAbort(abort)
 fetchReq.then(function (res) {
 resolve?.(res)
 item.status = 'done'
 })
 .catch(function (err) {
 item.status = 'error'
 reject?.(err)
 })
 .finally(function () {
 __do_fetch__()
 })
 })(first_init_item)
 }
 }
 request = (url: string, init: RequestInit & { abortHandler?: HttpAbortHandler }) => {
 const { props: { keyRender }, queue, __do_fetch__ } = this
 const key = keyRender(url, init)
 const has = queue.find(q => q.key === key)
 if (has) {
 throw new Error('fetch key duplicated!')
 }
 const item = new Promise(function (resolve, reject) {
 queue.push({
 key, url, init,
 status: 'init',
 resolve, reject,
 })
 __do_fetch__();
 })
 return item
 }
 get = (url: string, init: { abortHandler?: HttpAbortHandler } = {}) => this.request(url, init)
 post = (url: string, init: RequestInit & { abortHandler?: HttpAbortHandler } = {}) => this.request(url, { ...init, method: 'POST' })
}
export class HttpAbortHandler {
 abort: () => void
 setAbort = (abort: () => void) => this.abort = abort
}
/** 基于fetch的封装 */
export const REQUEST_FETCH: HttpClientProps['request'] = (url: string, init: RequestInit = {}) => {
 const controller = new AbortController()
 const fetchReq = fetch(url, {
 ...init,
 signal: controller.signal
 }).then(res => res.json())
 return [ fetchReq, function () { controller.abort() } ]
}
/** 基于xhr的封装 */
export const REQUEST_XHR:HttpClientProps['request'] = (url: string, init: RequestInit = {}) => {
 const xhr = new XMLHttpRequest()
 const fetchReq = new Promise(function (resolve, reject) {
 xhr.addEventListener('readystatechange', function (e) {
 if (xhr.readyState === XMLHttpRequest.DONE) {
 if (xhr.status >= 200 && xhr.status < 300) {
 try {
 const res = JSON.stringify(xhr.responseText)
 resolve(res)
 } catch (e) {
 reject(e)
 }
 } else {
 reject(xhr.status)
 }
 }
 })
 xhr.addEventListener('abort', reject)
 xhr.open(init?.method || 'GET', url)
 xhr.send(init?.body as XMLHttpRequestBodyInit)
 })
 return [ fetchReq, function () { xhr.abort() } ]
}

测试用例

test.ts

import { HttpAbortHandler, HttpClient, HttpClientProps } from "./http-client-module"
const logger = {
 log: (...args: any[]) => {
 console.log.apply(console, [new Date().toLocaleString(), ...args])
 }
}
/** 基于setTimeout封装 */
const REQUEST_TIMEOUT: HttpClientProps['request'] = (url: string, init: RequestInit = {}) => {
 let timer: number
 let _reject: (err: any) => void
 const abort = function () {
 clearTimeout(timer)
 _reject?.('abort!')
 }
 const fetchReq = new Promise(function (resolve, reject) {
 const mat = url.match(/([0-9.]+)$/)
 const timeout = mat ? Number(mat[1]) : 2000
 _reject = reject
 timer = setTimeout(function () {
 resolve(timeout)
 }, timeout)
 })
 return [ fetchReq, abort ]
}
// TEST
const test = function () {
 const client = new HttpClient({
 concurrency: 2,
 request: REQUEST_TIMEOUT,
 })
 client.get('/path-1?t=1000').then(() => logger.log('/path-1'))
 client.get('/path-2?t=3000').then(() => logger.log('/path-2'))
 client.get('/path-3?t=2050').then(() => logger.log('/path-3'))
 client.get('/path-4?t=1000').then(() => logger.log('/path-4'))
 client.get('/path-5?t=4000').then(() => logger.log('/path-5'))
 const handler = new HttpAbortHandler()
 client.get('/path-6?t=3000', {
 abortHandler: handler
 })
 .then(() => logger.log('/path-6'))
 .catch(err => logger.log('/path-6', err))
 setTimeout(() => {
 handler.abort()
 }, 4200);
}
test()

运行效果

作者:云香水识原文地址:https://segmentfault.com/a/1190000043392762

%s 个评论

要回复文章请先登录注册