场景描述
这里通过几张截图,描述场景,废话少说,直接上页面截图:
图1: 分类列表
通过这个组件可以选择分类,而且可以删除某个分类,也可以全部删除。
图2: 日均销售额预估图
(1)此处显示图1中所选择的分类对应的日均销售额预估值。
(2)图1中删除某个分类,或者全部删除,日均销售额预估列表也删除这几项。
(3)图1中新增某个或者某些分类,日均销售额预估列表也新增这几项,并通过请求获取分类对应的销售额预估
技术架构背景
react + redux + fetch
使用promise封装fetch请求
问题描述
1. 新增分类,发送请求获取日均销售额预估。该请求比较慢,且返回时间不可控。
比如快速点击新增了4个分类,请求是按照先后顺序发出的,但是最先发出的请求(包含1个分类时)可能返回最慢,而最后发出的请求(包含4个分类)却最早返回。
导致的问题是,最先发出的请求最后返回,页面显示的结果为最后返回请求的内容,也就是会显示只有一个分类。而图1分类列表的已选择内包含有4个分类。
见下图:
2. 删除操作,不发送网络请求。如果添加分类后,请求未返回时,执行了删除操作。那等请求返回后,被删除掉的分类还会显示到页面里。给用户的感觉就是 删不掉。。
思路描述
拦截历史请求。意思是快速发送10个请求,那么拦截前9次的请求,只处理第10个请求。
提出问题(关于ajax的abort和fetch的promise)
1. ajax中有一个abort方法,可以拦截请求:
var xhr = $.ajax(url);
xhr.abort();
2. 而fetch作为ajax的升级版,越来越多的浏览器已经支持他了,但是fetch暂时不能被取消...,因为没有对应的api。
这里用promise如何实现abort呢?
promise仅有两个完成态,resolved和rejected。一个可以当做success处理,另一个可以当做error处理。一旦一个请求得到了响应,也就是promise执行后,不能执行abort,要么进入success处理,要么进error处理。
这里我们可以在success和error处理代码中,捕获到需要abort掉的请求(前9次历史请求),进行数据的单独处理,比如捕获到是历史请求,则该请求返回成功后,不对返回数据进行渲染等。
因此,本文给出的解决方案,并没有真正拦截请求,而是对需要被拦截的请求,进行返回数据的特殊处理。那么如何对返回数据进行特殊处理呢,请继续往下看……
解决方案
步骤一:
1. 在store中设置一个时间戳state,比如timeStamp:0默认值;
2. 每次新增分类时,都在action中发送一个当前请求的时间戳tempTimeStamp,并更新state中的timeStamp
下图是action中发送时间戳的操作.红框部分是删除操作对应的action,
然后,在reducer中执行删除分类,和新增分类操作,更新state中timeStamp,
3. 请求返回,不论成功还是失败,都会比较该请求和state中的timeStamp,如果timeStamp > tempTimeStamp,那么不处理该请求(请求成功的操作见上图,请求失败时的操作见下图);
提出问题:添加分类请求未返回时,执行删除操作,请求成功返回后,被删除项依旧显示到页面上
步骤二:(代码见步骤一)
1. 删除操作不设置时间戳,单独写一个action(与新增分类不共用action)
2. 每次删除操作,都立即更新state列表。当新增分类请求返回成功时,将返回值,与state中已选择分类列表 进行比较,如果返回值.length > 已选择列表.length,那么说明执行了删除操作,这是过滤掉返回值中多出的项
提出问题:当添加10个模板,就会发出10个请求。拦截前9个请求后,将最后一个请求结果(不论成功或失败)显示到页面上。这里实际上并没有真正的拦截请求,而是对历史请求返回结果不做处理。那么当前9个请求中存在失败的情况时,还是会报错,该如何拦截这里的请求呢?
步骤三(代码见步骤一):
1. 这个问题可能一些人不会遇到,或者没有看懂什么意思。没有关系的,这个疑问点,是和代码结构有关的
2. 这里我说下我的相关代码。这里写了一个公共组件,用来处理action中type和types时,dispatch的顺序。如const [REQUEST, SUCCESS, FAILURE] = types; 当请求成功时,会通过next方法,进入SUCCESS对应的reducer代码执行;当请求失败时,会先通过next方法,进入FAILURE对应的reducer代码执行,然后会进入报错代码;
3. 这里如果想要阻止历史请求(前9次请求)失败时报错,那么就将该请求对应的FAILURE,通过判断,在报错代码中排除掉。并在FAILURE对应的reducer代码中捕获errStatus,通过判断时间戳是否为历史请求,如果时,则不做处理;如果不是,则进行错误处理;
在reducer中捕获error,并进行处理的代码如下:
在公共组件的报错代码中排除掉该action,即:FETCH_GOODS_DAILYSALES_FAIL,代码如下:
如果有哪里没看明白,可以留言询问哈,这里涉及代码略多,不知道是否表达明白,欢迎询问