一种带数据合法性校验的竞速请求逻辑封装
在前端 hybrid 应用内做“首屏接口数据预取”优化时,可能存在首屏接口数据竞速的场景,即希望能从 2 个数据源同时获取数据,并取最快的那份数据进行渲染。
但在此“数据竞速”场景下,由于我们需要对两个数据源返回的数据进行合法性校验,因此 promise 自带的方法不能完全适用,因为他无法满足我们“带数据合法性校验的竞速请求”的场景。这里我们探讨并给出一种实现。
场景解释
所谓“带数据合法性校验的竞速请求”是指的:我要同时发出 2 个请求,且希望尽快拿到最早返回的“符合数据合法性校验要求”的请求结果。
举例来说,假如我打开页面后,希望拿到一个数据,这份数据同时放到了“cdn 服务器地址”和“服务端后台地址”,于是我希望打开页面后立刻向 2 个地址发出请求并拿到最早的那个符合要求的结果进行展示。
这里所谓的“最早的符合要求”,是指的要满足 2 点要求:
- 它是尽可能早返回的那个数据,因为页面需要尽早拿到合法数据进行渲染。
- 返回的这个数据需要满足“数据合法性校验函数”。因为最先拿到的远程的数据不一定准确,所以我们要求必须返回那个“既合法,又早”的数据。
Promise 自带的方法
Promise 自带的有 Promise.race, Promise.all, Promise.any 等,但是却无法满足该场景。
- race 函数,确实可以实现竞速。但是它在任何一个请求最早 settle 的时候就会立刻交付结果。这个交付的结果可能是“失败的”结果。而我们期望如果最早的那个请求失败了,那应该向外交付后面那个结果。毕竟在页面渲染场景下:即使渲染慢一点,也不要 fail。
- all 函数。它需要等到所有 promise 全部敲定后,才会交付结果。这无法满足我们“尽快”的要求。
- any 函数。这是较新版本浏览器才支持的功能,它可以在任意一个 promise 能达到 resolve 的情况下则交付结果,否则会等其他 promise,直到有一个 resolve 或者全部都无法完成才交付结果。这个看起来能满足我们“尽快且成功的”要求,但是无法满足“尽快+合法”的要求。在我们场景下,还需要 any 函数中帮我们做一下数据合法性校验,若数据不合法,也应当继续等待其他请求。
自己实现第一版
思路
俩请求同时发出,但是响应的时候,要先校验是否合法,合法则认为成功,成功就立刻 resolve 尽量让外面赶紧使用这份成功数据; 若失败则要看看是否还要等,没必要等的情况下才 reject—也就是只有最后一个 promise 才可能 reject。
总结来看:
- 成功后,不管三七二十一,直接 resolve 即可。这里利用 promise 状态不变的特性,自然可以避免多次 resolve 发生。
- 失败后,需要看“是否需要继续等待”再决定 reject。所谓“是否有必要继续等”,是需要看看是否还有可以继续等待的其他 promise。实现上,可以靠计数来实现。
具体代码
1 | function raceForTheBest(requestOne, requestTwo, checkFunc) { |
上述代码中,requestOne 和 requestTwo 需要重复编写处理代码,所以我门把他抽到了 _onSuccess 和 _onError 函数里面来复用。
优化代码
上述代码 onSuccess 和 onError 其实也存在多次重复调用。实际上 requestOne 和 requestTwo 可以靠循环来生成 2 个独立的调用上下文,但我们代码就可以只写一次。
1 | // 这里借助一个循环也可以避免重复代码。 |
测试用例
1 | // 测试用例 |
不断修改 code 的值,或者修改 setTimeout 时间,多次尝试执行上述代码来进行测试。确保符合预期即可。