字节获取浅谈
如同所有优秀的拼图一样,一个网页浏览器由许多不同的部分组成。有些部分光鲜亮丽,比如你喜欢的 Web API。有些部分则不那么显眼,比如 HTML 解析和网页资源加载。
即使是那些不那么引人注目的部分,也需要大量工作来标准化它们在不同浏览器中的行为。例如,HTML 解析最初只提供:“给我 HTML,我就会给你一个文档。”现在,它在不同浏览器中要可靠得多,因为它已经在细节上进行了标准化。同样,网页资源的加载在某种程度上保持一致,直到:“给我一个 HTTP 请求,我就会给你一个 HTTP 响应。”但加载网页资源所包含的内容远不止这些。Fetch 规范彻底地标准化了这些细节。除了指定浏览器如何加载资源外,Fetch 规范还定义了一个用于加载资源的 JavaScript API。这个 API,即 Fetch API,是 XMLHttpRequest 的替代品,在网页上下文中提供了最低级别的选项集。让我们看看 Fetch API 可能有多么闪亮。
Fetch API
Fetch API 由一个返回 Promise 的方法 fetch
组成。返回的值是一个 Response
对象,其中包含响应头和主体信息。让我们使用 Fetch API 来检索 WebKit 功能列表
async function isFetchAPIFeelingGood() {
let webkitFeaturesURL = "https://svn.webkit.org/repository/webkit/trunk/Source/WebCore/features.json";
let response = await fetch(webkitFeaturesURL);
let features = await response.json();
return features.specification.find((feature) =>
feature.name == "Fetch API");
}
isFetchAPIFeelingGood().then((value) => alert(!!value ? "Oh yes!" : "not really!"))
你可能注意到上面例子中有两个 await
用法。fetch
返回一个 Promise,当接收到响应头时该 Promise 会被解析。请求的数据是 JSON。第二个 Promise 在整个响应主体可用时解析。
fetch
可以接受 URL
或 Request
对象。与 XMLHttpRequest 相比,Request
对象允许访问一组全新的选项。让我们再次尝试检查 WebKit 是否支持 Fetch API,但这一次,我们要确保缓存不会为我们提供过时信息。
async function isFetchAPIFeelingGoodForReal() {
let webkitFeaturesURL = "https://svn.webkit.org/repository/webkit/trunk/Source/WebCore/features.json";
let response = await fetch(new Request(webkitFeaturesURL,
{ cache: "no-cache" }
));
let latestFeatures = await response.json();
return latestFeatures.specification.find((feature) =>
feature.name == "Fetch API");
}
fetch
还提供了对响应主体的更灵活访问。除了以各种格式(JSON、arrayBuffer、blob、text 等)获取它之外,响应还提供了 ReadableStream
主体属性。这使得在字节块到达时能够逐步处理它们,无需缓冲整个数据,甚至可以中止资源加载。
async function featureListAsAReader() {
let webkitFeaturesURL = "https://svn.webkit.org/repository/webkit/trunk/Source/WebCore/features.json";
let response = await fetch(new Request(webkitFeaturesURL));
return response.body.getReader();
}
function checkChunk(searched, buffer, count)
{
var i = 0;
while (i < buffer.length) {
if (buffer[i++] == searched.charCodeAt(count)) {
if (++count == searched.length)
return count;
} else if (count) {
--i;
count = 0;
}
}
return count;
}
async function isFetchAPIFeelingGoodWhileChunky(reader, count)
{
reader = reader ? reader : await featureListAsAReader();
count = count ? count : 0;
let chunk = await reader.read();
if (chunk.done)
return false;
let searched = "Fetch API";
count = checkChunk(searched, chunk.value, count);
if (count == searched.length)
return true;
return isFetchAPISupported(reader, count);
}
Fetch 的未来
Fetch API 的发展之路尚未结束。新的提案可能会涵盖 Fetch 目前缺乏的 XMLHttpRequest 的重要功能,例如早期取消和超时。新的提案还可能涵盖 HTTP/2 推送和优先级,以及在 Web API 中更广泛地使用 Response 对象:媒体元素、Web Assembly 等。Fetch 算法也在不断完善,以实现网页资源加载的完全互操作性。WebKit Fetch API 的首个实现版本已在 Safari 中发布。WebKit 社区渴望听到您对此功能的反馈。欢迎通过常规的 WebKit 渠道提供评论、建议、优先级、用例、测试、错误报告和糖果。那真是太棒了!