fetch的入口函数定义在node_modules/whatwg-fetch.js文件中,如下
self.fetch = function (input, init) {
return new Promise(function (resolve, reject) {
var request = new Request(input, init)
var xhr = new XMLHttpRequest()
xhr.onload = function () {
var options = {
status: xhr.status,
statusText: xhr.statusText,
headers: parseHeaders(xhr.getAllResponseHeaders() || '')
}
options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL')
var body = 'response' in xhr ? xhr.response : xhr.responseText
resolve(new Response(body, options))
}
xhr.onerror = function () {
reject(new TypeError('Network request failed'))
}
xhr.ontimeout = function () {
reject(new TypeError('Network request failed888888888888'))
}
xhr.open(request.method, request.url, true)
if (request.credentials === 'include') {
xhr.withCredentials = true
}
if ('responseType' in xhr && support.blob) {
xhr.responseType = 'blob'
}
request.headers.forEach(function (value, name) {
xhr.setRequestHeader(name, value)
})
xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit)
})
}
该函数在Network/fetch.js中被导出,最终在initializeCore.js中被注册为global的属性变为全局函数。fetch返回的是一个Promise。
跟随方法走向,依次调用的是XMLHttpRequest.js的send -> RCTNetworking.ios.js的sendRequest -> 最终调到原生端RCTNetworking.mm的sendRequest方法。
相关问题点:
1、为何fetch函数无法设置timeout?
为了设置fetch的timeout,我会如下定义一个函数
_timeout_fetch(fetch_promise, timeout = 15000) {
let timeout_promise = new Promise(function (resolve, reject) {
setTimeout(() => {
reject('timeout promise');
}, timeout)
});
return Promise.race([
fetch_promise,
timeout_promise
]);
}
然后如下调用
this._timeout_fetch(
fetch(url, requestParams)
.then(response => response.json())
.then(responseData => {
resolve(responseData);
})
.catch(error => {
reject(error);
})
)
先定义一个Promise,其在超时时间结束后reject。将这个Promise和fetch合并到Promise.race中,则一旦这两个请求谁先执行,另外一个会被舍弃。这样完成超时时间的设置。
但是查看源码发现oc中是有超时时间设置这个选项的,且js和oc通信时也传了这个参数,问题是出在入口函数fetch处,只需要在上面fetch方法中添加上如下
xhr.timeout = init.timeout || 0;
就可以在请求参数中设置超时时间了,如
let requestParams = {
method: method,
header: {
"Content-Type": "application/json;charset=UTF-8",
},
timeout: 1000
};
2、fetch函数为何无法cancel?
fetch在原生端是NSURLSessionDataTask发的请求,这个是可取消的。在js端的XMLHttpRequest.js中也发现了abort方法,调用能够取消当前网络请求。问题出在了fetch的接口函数。
首先,要想请求能够取消,得拿到当前请求对应的requestId。请求的执行顺序是js端发起 -> OC生成Request,得到requestId,利用NSURLSessionDataTask发起请求 -> 将requestId通过回调的形式传回给js端,js若想取消该请求,执行abort方法即可。
要想fetch函数能够执行cancel,只需该方法将XMLHttpRequest对象返回即可。但是那样,就不再是一个Promise了。
也可以将cancel方法绑定到返回的Promise对象上,修改方法如下
self.fetch = function (input, init) {
var xhr = new XMLHttpRequest()
let p = new Promise(function (resolve, reject) {
var request = new Request(input, init)
// xhr的各种设置,回调等
})
p.cancel = () => {
xhr.abort()
}
return p;
}
如此,调用的时候就比较恶习了,要如下
let promise = fetch(url);
promise.then(res => {
}).then(res => {
}).catch(err => {
})
promise.cancel() // 取消该网络请求
不能fecth().then().then()的模式调用,因为这样会导致返回的那个Promise不再是上面绑定了cancel的那个Promise。