今天面试官问的我这个问题,说实话,我当时懵逼了。
我第一个想法竟然是:嘶~这问题挺简单的啊,不就是用 Date
对象判断是否大于某个时间差么?于是写出了下面这段代码:
let rest=function(){
// 开始是放在外面的,但是面试官说要尽可能不单独暴露值,于是鬼迷心窍(紧张)杀都没想就直接扔进来了...
let date=Date.now();
return new Promise((resolve,reject)=>{
let _date=Date.now();
if(_date-date>2000){
reject('请求超时');
}
resolve();
})
}
在我暗自得意是不是快结束了面试官一会儿会如何告诉我面试通过了的时候,对面突然传来一句话:
“你这个,确定要这样?”
“你这样if条件真的会执行么?”
我顿时感觉大事不妙大惊失色,正要仔细看时面试官已经说出了“行吧,这道题你再想想,这次的面试就到这里吧”
下来后仔细想想,“基于promise”的前提就已经提示了应该要“尽可能地应用promise的特性”,那么,这个问题应该用什么呢?
我想起了前两天写过的这篇文章:实现一个“能中断的”ajax 。里面提到了可以用 “ reject抢跑 ”的方法去中止后续回调的执行!
这里要记得一点:promise状态值改变一旦触发就不可逆,所以不可能真正“中断”promise,只能说通过同步与异步的先后规则(event Loop)去让“成功”的回调无法执行。
我似乎恍然大悟了起来…
但现在还有一个问题:怎么判断“超时”?
如果不出意外,你把两个 Date
放在一个函数中无论如何它们的值都是相同的!所以我想到了 setTimeout
!有了这个异步时间处理,我的思路就明朗了起来:在promise中有一个API叫:Promise.race()
,和 all()
不同的是,race在其中promise状态值有一个改变时就会立刻执行它的结果,就像这样:
let rest=function(_data=1000){
return Promise.race([
upload().then(data=>{console.log(data.data)}),
uploadTimeout(_data)
])
}
function upload(){
console.log('请求进行中...');
return new Promise((resolve,reject)=>{
let xhr=new XMLHttpRequest();
xhr.open('GET',"https://devapi.qweather.com/v7/weather/24h?location=这里是纬度和经度英文逗号分搁&key=这里是百度地图的key");
xhr.onload=function(){
if(xhr.readyState==4 && (xhr.status>=200 && xhr.status<300)){
setTimeout(()=>{
resolve({data:JSON.parse(xhr.responseText)})
},2000)
}else{
reject(xhr.status)
}
}
xhr.onerror=function(){
reject('请求失败了...')
}
xhr.send(null);
// 【1】
})
};
function uploadTimeout(times){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
reject('请求超时,请重试'); // 【2】
},times)
})
}
到这里,基本实现了功能,但是运行后你会发现:第一个函数在报错后仍然执行了!
对,这就是上面说的:promise状态值改变的过程是不可逆的。而且虽然你下面返回了reject,但是这是两个promise,之间是不冲突的!
受上面说过的 reject抢跑 的启发,我们可以在代码中标注为【1】的地方写这样的代码:
// 向外暴露取消函数
cancelFn=function(msg){
reject('请求超时,请重试');
}
然后将代码中标注为【2】的地方的代码替换为:
cancelFn();
至此,一个“超时请求处理”的功能就真的实现了: