美文网首页React Native
React Native fetch实现timeout功能

React Native fetch实现timeout功能

作者: Daniel_颜世玉 | 来源:发表于2017-03-12 15:26 被阅读2756次

    前几天收到一个需求,就是在网络请求超时时,弹出一个toast告知用户“网络请求超时,请稍后重试!”。当接到这个需求时,一开始并没有觉得有多麻烦,直接在fetch请求时设置一下timeout属性就行了。于是乎就去React Native的开发文档中去找,可结果却让人“很难过”,居然没有找到,心里不免嘀咕“又掉到坑里面了”。

    好吧,既然没有timeout属性就只能自己去想办法解决了。经过一段时间的思考,初步确定了三种解决方案:

    • 通过和原生的交互来实现网络请求超时的功能;
    • 把fetch重新封装一下,加上timeout的功能,参考博文
    • 使用其他的网络库,文档中说React Native中已经内置了XMLHttpRequest API(也就是俗称的ajax);

    在项目中我选择了第二种方法,具体的实现如下:

    //Http.js
    export default fetchers = {
        post:(url, body = {}) => {
            return _fetch(fetch_promise(url, body = {}), 60000);
        }
    }
    
    function _fetch(fetch_promise, timeout) {
        var abort_fn = null;
        var abort_promise = new Promise((resolve, reject) => {
            abort_fn = function() {
                reject('abort promise');
            };
        });
        var abortable_promise = Promise.race([
            fetch_promise,
            abort_promise
        ]);
        setTimeout(function(){
            abort_fn();
        }, timeout);
    
        return abortable_promise;
    }
    
    function fetch_promise(url, body = {}) {
        return new Promise((resolve, reject) => {
            fetch(url,{
                method: 'POST',
                headers: {
                    'Accept': 'application/json',
                    'Content-Type': 'application/json;charset=UTF-8',
                },
            }).then((response) => { 
                return response.json();     
            }).then((jsonData) => {
                resolve(jsonData);
            }).catch((err) => {
                reject(err);//这里可以使用resolve(err),将错误信息传回去
                if (err.message === 'Network request failed'){
                    console.log('网络出错');
                } else if (err === 'abort promise'){
                    console.log('请求超时');
                }
            })
        })
    }
    
    /*
     * 此巧妙之处在于Promise.race()的使用
     */
    

    这是目前项目中初步解决网络超时的方法,第一种和第三种方案还没有来得及去实现,后期如果有时间实现会在此后加上。如果发现有需要修改的地方或是好的建议,也可以留言告知,大家相互交流相互提高嘛!

    参考链接:

    相关文章

      网友评论

      • 不知所语:我想问下你,Promise.race后返回的promise不应该加上catch方法吗,如果超时了,reject('abort promise'),不应该执行catch吗,我可能理解的不太到位,希望指正
        不知所语:@Daniel_颜世玉 :joy: 哈哈,这个就尴尬了,我就郁闷这里。。。
        Daniel_颜世玉:这个catch在fetch_promise方法中的promise上接受到了,具体为啥现在我也忘了怎么解释了:stuck_out_tongue_winking_eye:
      • 咸湿仔灬:看了作者的回复,好用心也很有耐心,是个好的程序猿:grin:
        Daniel_颜世玉:@咸湿仔灬 现在已经写回原生了,暂时不写RN的代码了
        咸湿仔灬:@Daniel_颜世玉 要多写react-native相关的博客哦,已关注了:stuck_out_tongue_closed_eyes:
        Daniel_颜世玉:谢谢夸张,已经好久没写技术博客:sweat_smile:
      • 3d48a6ea5f7a:promise我不懂,但是如果一直是network request failed,然后超时时间设置了也没用的话,应该是_fetch中的reject没有被捕获,改成以下这样就行了:

        function fetch_promise(url, body = {}) {
        return new Promise((resolve, reject) => {
        _fetch(fetch(url,{
        method: 'POST',
        headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json;charset=UTF-8',
        },
        })).then((response) => {
        return response.json();
        }).then((jsonData) => {
        resolve(jsonData);
        }).catch((err) => {
        reject(err);//这里可以使用resolve(err),将错误信息传回去
        if (err.message === 'Network request failed'){
        console.log('网络出错');
        } else if (err === 'abort promise'){
        console.log('请求超时');
        }
        })
        })
        }
        3d48a6ea5f7a:@Daniel_颜世玉
        我的方案跟你不一样,结果是能出来的。我是看有些人报的错(只有报network request failed,并且黄色提示框一直提示有reject未处理)跟我编程过程中的报错很相似,给一个解决问题的方案而已。
        Daniel_颜世玉:你应该还没有看明白这个方法,fetch_promise(url, body={}) 只是 _fetch(fetch_promise, timeout)的参数而已,真正的使用是调用 fetchers.post。

        引用:
        import fetchers from '../../utils/Http';

        调用:
        fetchers.post(`${queryStrings.QUERY_ECHARTS_DATAS + reqRefs}`, '')
        .then(data => dispatch(receiveInvestmentP11(data)));
      • 彼岸青园:怎么使用呢?能不能给个代码示例,初学者:cold_sweat:
        Daniel_颜世玉:@Daniel_颜世玉 超时时间,在Http中默认设为了60000毫秒(一分钟),这里可以根据需要自己修改。希望能帮助到你!
        Daniel_颜世玉:引用:
        import fetchers from '../../utils/Http';

        调用:
        fetchers.post(`url`, '{params}')
        .then(data => console.log(data)));
      • f86fb9a8ce6a:这个是我看了别人的该的race ,主要是项目中要弹出 超时的信息提示

        Promise.race([
        this.fetchPromise(url, oldPassword, newPassword),
        timerPromise
        ]).then(
        (res) => console.log(res), //接收上面resolve中的参数
        (error) => {console.log(res)} //接收上面reject中的参数
        );
        Daniel_颜世玉:如文中所写,reject(err);//这里可以使用resolve(err),将错误信息传回去。然后在接受的地方弹出错误信息,这里的类没有继承Component,除了alert,没法谈自定义的错误框,所以回调回去比较合适。
      • f86fb9a8ce6a:reject 里面不能做弹出吗? 如果只放字符串 顶多只会在下方出现黄色警告, 如果用alert 那不管另一个promise是否先得到结果 alert都会运行,有没有什么办法可以reject弹出
      • f86fb9a8ce6a:看了你写的才看懂,比较有条理,之前看别人写的我就不理解为什么封装fetch就要加入timeout参数
      • 小猪向天飞:作者,你好!我的问题恰恰是相反的,无论怎么设置时间,都是马上返回错误信息的,根本就延时不了:flushed:
        Daniel_颜世玉:你的请求应该是出错了,有错误返回时,是马上返回错误信息的,这个是不需要超时处理的,只有网络没有正确或者错误返回时,才会最终超时。
      • 自由行走_9c2d:我参照着你的代码写了,但是请求不到的时候还是要很久才反应,就算我设置了时间。而且在catch里面,err的值一直是network request failed
        自由行走_9c2d:@Daniel_颜世玉 嗯,我就是故意修改了地址,那请求不到url了,timeout 不起作用吗?
        Daniel_颜世玉:@自由行走_9c2d 你这个就是网络出错了,所以才会报这个err.你在看看你的请求头是不是和我的一样
        Daniel_颜世玉:@自由行走_9c2d 在吃饭中,回去我看看
      • 风雨vs同舟:写的很漂亮,正好需要,很赞

      本文标题:React Native fetch实现timeout功能

      本文链接:https://www.haomeiwen.com/subject/diregttx.html