美文网首页
你以为location有多靠谱?

你以为location有多靠谱?

作者: 寂寞的原子 | 来源:发表于2018-10-16 01:06 被阅读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也不是那么的靠谱。

    相关文章

      网友评论

          本文标题:你以为location有多靠谱?

          本文链接:https://www.haomeiwen.com/subject/ssfczftx.html