不愧是前端老油条,分分钟看出我方案的bug
共 2996字,需浏览 6分钟
·
2021-10-10 16:14
点击上方关注 前端技术江湖,一起学习,天天进步
国庆前刚开发完一个小需求,常规性的做了一次code review,不过这次review有所不同,我们组前端老油条竟然参会了,平时发会邀都不来的。
不过不愧是老油条,竟然分分中发现了问题,老油条的地位又在我们小前端的心里巩固了一下。
和往常一样,review前先过一遍技术方案,一让大家快速的了解需求,二来分析下技术方案是否存在问题,是否合理,一般情况下,技术方案没问题,后面的代码review感觉就没啥必要了,因为很少有人听。
就在刚要讲完技术方案,要进入review代码阶段,老油条提出了问题,我心想这么简单的需求会有啥问题?
到这里我不得不把需求搬出来,让各位看官瞅瞅,一起来看是否存在这样的问题,换作是你,你会怎样处理。
需求超简单,上截图
上面是一个分类数据展示页面,左侧是一级分类,右侧二级分类和三级分类,然后下面是推荐的商品列表。
就这样一个页面会有什么问题呢?撑死2个接口,一个分类接口,一个商品推荐接口。
问题在于交互上,左边的一级分类可以随意切换,如果用户不讲武德,不等右边的数据渲染完就切换了,那会给用户带来什么体验呢?
会导致渲染结果混乱,旧数据渲染在了新的分类里。
解决方案
其实很简单,切换分类时,取消上一个请求。
怎样实现取消请求呢?
下面咱们就来总结性的看下如何取消一个请求,看哪个方式更简单易用。
思路:项目中的网络请求模块一般都是基于promise进行封装,返回一个promise对象,其实只要把 promise 取消掉就可以,其实并不是真正的取消网络请求,而是丢弃返回的数据。
方法1 - 借助reject方法
fe都知道一个promise对象状态的改变是通过resolve和reject来执行的。那是不是可以借助reject方法来模拟呢?
//返回一个promise和abort方法
function getPromise() {
let _res, _rej;
const promise = new Promise((resolve, reject) => {
_res = resolve;
_rej = reject;
setTimeout(() => {
resolve('123')
}, 5000);
});
return {
promise,
abort: () => {
_rej({
name: "abort",
message: "the promise is aborted",
aborted: true,
});
}
};
}
const { promise, abort } = getPromise();
promise.then(console.log).catch(e => {
console.log(e);
});
abort();
上面的方法可以正常执行,但是不够通用,可以将Promise构造函数内的逻辑提取出来,作为一个回调传进去。
改造后
function getPromise(cb) {
let _res, _rej;
const promise = new Promise((res, rej) => {
_res = res;
_rej = rej;
cb && cb(res,rej);
});
return {
promise,
abort: () => {
_rej({
name: "abort",
message: "the promise is aborted",
aborted: true,
});
}
};
}
//主逻辑提取出来
function runCb(resolve,reject){
setTimeout(()=>{
resolve('1111')
},3000)
}
const { promise, abort } = getPromise(runCb);
promise.then(console.log).catch(e => {
console.log(e);
});
方法2 - 借助 Promise.race()
简单复习下race方法的作用,当有若干个promise, p1, p2, p3…在调用, let p = Promise.race([p1, p2, p3,…])的时候,返回的p也是一个promise。那么p什么时候会被resolve或者被reject呢?
看race单词的意思就知道它是竞速或赛跑的意思,所以p1, p2, p3 … 最先一个被resolve或者被reject的结果就是p的resolve或者reject的结果。所以后续的promise的resolve和reject都不会再被执行了。
代码很简单,但足够短小精悍。
//传入一个正在执行的promise
function getPromiseWithAbort(p){
let obj = {};
//内部定一个新的promise,用来终止执行
let p1 = new Promise(function(resolve, reject){
obj.abort = reject;
});
obj.promise = Promise.race([p, p1]);
return obj;
}
调用~
var promise = new Promise((resolve)=>{
setTimeout(()=>{
resolve('123')
},3000)
})
var obj = getPromiseWithAbort(promise)
obj.promise.then(res=>{console.log(res)})
//如果要取消
obj.abort('取消执行')
借助race方法明显的更简洁,更易用。
最后
取消promise执行和取消请求是一样的,并不是真的终止了请求的执行,而是丢弃了数据,对结果不再处理。另外fetch api虽然增加了新的标准实现(AbortSignal),但仍然存在兼容问题,而且只能在浏览器中使用。那么非浏览器的环境中呢?比如RN?小程序?所以如果想要达到一种通用的方式,那么本文的reject promise的方式应该是个不错的方式。
此时我看着11点方向的前端老油条,肃然起敬,摆正自己的态度,多向前辈学习,做了这么多年的业务还能保持这样的敏锐度。
拿起手机疯狂的自测起自己的需求,忽然发现了一个性能问题,频繁切换竟然会卡。
为啥卡?怎么解决?
欢迎留言讨论。
The End
欢迎自荐投稿到《前端技术江湖》,如果你觉得这篇内容对你挺有启发,记得点个 「在看」哦
点个『在看』支持下