你以为location有多靠谱?

2018-10-16  本文已影响45人  寂寞的原子

这里说的是网页上的window.location,用户获取和操作网页地址的一个强大的JS对象。

背景

我们知道,用户从一个网页跳转到另一个网页时,location就会发生变化。同样的,如果我们对location进行修改,或者调用对应的函数,我们就期望页面发生跳转。比如:

  1. location跳转
    window.location.assign('https://www.google.com');
    
  2. location替换
    window.location.replace('https://www.google.com');
    

那么问题来了,页面一定会跳转吗?

现实

最近遇到了一个问题,就是当用户提交了表单之后,所有代码都正常执行了,但是过了几秒,用户又提交了一次。结果因为已经提交过了,重复提交就会一直失败,用户就在那不停地点啊点啊,最后绝望而去。

整段代码已经简单到已经没有多少想象空间了:

try {
  await submit();
  window.location.replace('/success');
} catch (err) {
  trackError(err);
}

就酱,用户第一次提交并没有报错,却没有跳转到成功页面,而导致了重复提交。也就是说,第一次提交成功了,location.replace也成功执行了,但是却没有跳转。

为了确定我的猜想,我加了更多的上报:

track('start');
try {
  await submit();
  window.location.replace('/success');
  track('success');
} catch (err) {
  trackError(err);
}
track('finish');

结果,在收到报错的情况下,果然前一次提交的startsuccessfinish都上报了。也就是说,所有代码都正常运行了,页面却没有跳转!

image

问题只能出在location上了。所以这个原生方法关键时刻掉链子了吗?

实际上,这里有一个重要的问题我没有意识到:location的赋值是同步的,但是页面的加载却是异步的。也就是说,代码已经执行完了,浏览器才在后台异步地加载新页面,一般来说,链接建立并开始传输数据,页面才会发生跳转。

经过试验,发现跳转过程中浏览器有如下特点:

  1. 如果连接失败,有的浏览器会迅速跳转到一个错误页面,有的浏览器会卡住很长时间,整个页面可操作
  2. 如果尚未连接成功,浏览器会一直停留在老页面上,整个页面可操作
  3. 如果连接成功,但是数据未完全加载,浏览器会跳转到新页面,并渲染已有数据(或者白屏)。

所以,如果浏览器表现为正在连接,而且整个页面是可操作的,那就坑大了。

通过一些其他不可描述的手段,我终于发现,用户确实是因为网络问题,加载/success页面失败了。结果用户就在原来的页面上继续提交,一直失败。

那么,如何防止用户重复提交以及友好地提示失败呢?

防御

我们预期的操作应该是,提交成功后页面跳转,提交失败时允许用户重试。那么如何防止出现上面的问题呢?

  1. 可以显示加载中的提示,防止用户频繁操作,然后加一个timer,等待页面跳转。如果页面跳走了,timer也就失效了。
  2. 可以给 submit 方法添加一个标记,如果已经成功提交了,下次就直接尝试跳转。当然,这里其实更应该由后端做防御,前端也可以处理一下以避免产生误导用户的错误提示,比如前面的无止境的“提交失败”。

最终代码如下:

const delay = time => new Promise(resolve => setTimeout(resolve, time));

let submitResult;
async function memoizedSubmit() {
  if (submitResult) return submitResult.result;
  const result = await submit();
  submitResult = { result };
  return result;
}

showLoading();
try {
  await memoizedSubmit();
  window.location.replace('/success');
  await delay(5000);
  console.log('阿欧,5秒钟后仍然没有跳走,看来是失败了。');
  // 这里可以做一些友好的提示
} catch (err) {
  trackError(err);
}
hideLoading();

总结

其实这个问题归根结底是页面的异步加载加上网络的问题,而网络的问题是防不胜防的,我们永远无法预计到用户什么时候进个电梯、上个厕所、换个姿势,网络就断了,但是我们要保证的是网络恢复后,流程还可以走下去。

最后,location也不是那么的靠谱。

上一篇下一篇

猜你喜欢

热点阅读