fetch-event-source (fetch)分析: 文件里导出的有EventStreamContentType/FetchEventSourceInit/fetchEventSource,前面两个分别是contentType和ts声明,不是主要的,我们主要是需要了解fetchEventSource方法
fetchEventSource是什么
function fetchEventSource(input: RequestInfo, {
signal: inputSignal,
headers: inputHeaders,
onopen: inputOnOpen,
onmessage,
onclose,
onerror,
openWhenHidden,
fetch: inputFetch,
...rest
}: FetchEventSourceInit) {
return new Promise((resolve, reject)=> {
async create() {
fetch(url, {...other})
}
create()
})
}
将fetchEventSource方法提炼如上所示,上面代码可以看出fetchEventSource主要是根据传入参数进行逻辑组装发起请求,返回promise对象。
- signal: 见下面疑惑分析
- headers: 主要处理请求头,同时设置请求的accept类型为'text/event-stream'
const headers = { ...inputHeaders };
if (!headers.accept) {
headers.accept = EventStreamContentType;
}
fetch(url, {headers, ...other})
- onopen: 当接口请求正常返回会执行该方法
const response = await fetch(input, {
...rest,
headers,
signal: curRequestController.signal,
});
await onopen(response);
- onmessage:在parse中学习getBytes/getLines方法后才会知道onMessage做什么用,此时只知道接口成功返回后执行
const response = await fetch(input, {
...rest,
headers,
signal: curRequestController.signal,
});
await onopen(response);
await getBytes(response.body!, getLines(getMessages(id => {
if (id) {
// store the id and send it back on the next retry:
headers[LastEventId] = id;
} else {
// don't send the last-event-id header anymore:
delete headers[LastEventId];
}
}, retry => {
retryInterval = retry;
}, onmessage)));
- onclose: 也是接口返回成功执行
- onerror: 接口返回失败执行
try {
const response = await fetch(input, {
...rest,
headers,
signal: curRequestController.signal,
});
} catch(e => {
onerror(e)
})
- openWhenHidden: 窗口切换关闭和开启请求
function onVisibilityChange() {
curRequestController.abort(); // close existing request on every visibility change
if (!document.hidden) {
create(); // page is now visible again, recreate request.
}
}
if (!openWhenHidden) {
document.addEventListener('visibilitychange', onVisibilityChange);
}
}
- fetch: 请求后端接口的方法,如果不传,默认是window.fetch;否则采用传入的方法
- rest: 接口请求的其它参数
const response = await fetch(input, {
...rest,
headers,
signal: curRequestController.signal,
});
通过分析传入的参数,会有几个疑惑,第一signal是什么;第二onOpen/onMessage/onClose都是接口请求完执行,这两个分别处理什么。
疑惑分析
signal
fetch.ts文件中除了传入的signal,在fetch请求中也传了signal, 它主要是控制器对象AbortController, 用于中止一个和多个web请求。
curRequestController = new AbortController();
const response = await fetch(input, {
...rest,
headers,
signal: curRequestController.signal,
});
当inputSignal监听到中止abort方法,会移除相关方法和定时器,同时中止当前fetch请求。猜测传入的inputSignal和fetch中的signal类似,用于中止请求, 譬如chatgpt消息正在显示(打字机效果),此时点击中止按钮,执行inputSignal.abort()方法,阻止内部fetch执行
function dispose() {
document.removeEventListener('visibilitychange', onVisibilityChange);
window.clearTimeout(retryTimer);
curRequestController.abort();
}
// if the incoming signal aborts, dispose resources and resolve:
inputSignal?.addEventListener('abort', () => {
dispose();
resolve(); // don't waste time constructing/logging errors
});
onOpen/onClose/onMessage区别
首先执行onOpen,告诉用户fetch接口请求执行完成。为请求是text/event-stream类型,数据不是一次就获取完成,因为通过onMessage依次来获取,同时也可以实现打印机输出的效果,具体分析见另一篇文章fetch-event-source(parse),当所有的onMessage执行完成后,执行onClose方法