react-native——fetch

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。

你可能感兴趣的:(react-native——fetch)