js异步请求时序问题

问题引入

开发过程中会遇到类似下拉框后端搜索的功能,此时键入key,onChange会频繁触发,可能导致
①频发无用请求(仅当用户停止输入,才有可能是最终想要搜索的key)
②导致请求时序错乱(即一前一后两个请求,后请求的已返回,而前请求的未返回)

防抖与节流

防抖

从开始执行方法时计时,每再执行一次方法,重新计时。如:设定防抖时间200ms,则在搜索框应用场景内,仅当用户输入停顿达到两秒,才会发送请求,一定程度上避免过度请求带来的问题

原生实现

// 防抖
function debounce(fn, wait) {    
    var timeout = null;     // 利用闭包存住timeout
    return function() {        
        if(timeout !== null) {
            clearTimeout(timeout);
        }    
        timeout = setTimeout(fn, wait);    
    }
}

React hooks版

import { useEffect, useRef } from 'react'

const useDebounce = (fn, ms = 30, deps = []) => {
    let timeout = useRef()
    useEffect(() => {
        if (timeout.current){
           clearTimeout(timeout.current);
        }
        timeout.current = setTimeout(() => {
            fn()
        }, ms)
    }, deps)

    const cancel = () => {
        clearTimeout(timeout.current)
        timeout = null
    }

    return [cancel]
  }

export default useDebounce;

// 用法
import { useDebounce } from 'hooks'
const Home = (props) => {
  const [a, setA] = useState(0)
  const [b, setB] = useState(0)
  const [cancel] = useDebounce(() => {
    setB(a)
  }, 2000, [a])

  const changeIpt = (e) => {
    setA(e.target.value)
  }
  return <div>
    <input type="text" onChange={changeIpt} />
    { b } { a }
  </div>
}

节流

节流思路为,若设置的时间为200ms,则200ms间隔时间未到,不会再次执行函数。

原生实现

// 节流
function throttle(func, ms) {
    let previous = 0;
    return function() {
        let now = Date.now();
        let context = this;
        let args = arguments;
        if (now - previous > ms) {
            func.apply(context, args);
            previous = now;
        }
    }
}

偷懒版

lodash

import {  debounce, throttle } from 'lodash';
debounce(() => { 
  //DO SOMETHING
}, 200);

throttle (() => { 
  //DO SOMETHING
}, 200);

解决请求时序问题

防抖与节流并不能100%解决异步请求过于频繁带来的请求response错乱的问题,当两个相同请求,响应时间差异过大第一个请求慢于第二个请求返回,此时若不作处理,正常流程走完以后会发现第一个请求的结果覆盖了第二个请求,此时需要对结果做处理,即抛弃第一个请求的结果

实现思路

这里以dva.js为例
1.我们需要两个容器存储 最后一次请求的时间(lastResponseTime最新请求的结果(responseCache)
2.当一个请求,如 *getXXX()开始时,获取当前时间currentTime
3.当异步请求执行完毕,将currentTimelastResponseTime作对比,当currentTime>lastResponseTime,则说明该请求的结果为最新的,将其存入responseCache并更新lastResponseTime = currentTime 并使用当前请求的结果;否侧弃用当前请求的结果,沿用responseCache

state: {
    lastResponseTime: new Date(),
    responseCache: undefined,
},
*getXXX({ payload, callback }, { call, put, select }){
  const currentTime = new Date().getTime();
  const response= yield call(getXXX, payload);
  const { lastResponseTime} = yield select(_ => _.MODULE_NAMESPACE_XXX);
  if(currentTime > lastResponseTime){
    yield put({
            type: 'updateState',
            state: {
              responseCache: response,
              lastResponseTime: currentTime ,
            },
      });
      callback(response);
  } else {
        const { responseCache } = yield select(_ => _.MODULE_NAMESPACE_XXX);
        callback(responseCache);
  }
}

另一种思路

上面的实现思路是控制请求结果,我们还可以从请求开始出发。
即,当上一个请求未返回,则不发送下一个请求,直到上一个请求结束。
该种情况不适用于搜索,因为搜索更注重结果,所以需要取最新一次请求的结果。此种情况适用于状态轮询的应用场景,如小明正在吃早餐,当我们需要知道小明是否已经吃完了,隔两分钟就去询问一下,如果其中一次小明还未回复,则不再问他了

可以借助dva.js的loading状态来判断是否正在pending

setInterval(()=>{
  if(!getXXXLoading){
      dispatch({
        type: MODULE_NAMESPACE_XXX/getXXX,
        payload:{},
        callback: ()=>{}
    })
  }
}, 200)

注意dva.js的loading存在问题:依次发送两个相同请求,当其中一个返回,loading状态就为false。
1.定义不同的effects
2.手动维护loading状态
持续关注该问题是否已解决

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容

  • css相关 1. 万能居中 1.margin: 0 auto;水平 2.text-align: center;水平...
    chaocc阅读 948评论 0 2
  • css相关 1. 万能居中 1.margin: 0 auto;水平2.text-align: center;水平3...
    宁_Yi阅读 3,340评论 2 39
  • https://www.jianshu.com/p/769266edbd38 前言 经过艰苦卓绝的面试历程,终于拿...
    Clover园阅读 223评论 0 0
  • 1、离职多久了 2、大约多久到岗 3、离这里多远 4、会考虑搬家吗 5、公司会加班 6、为什么要离职 7、你们这个...
    临渊鲸落阅读 1,206评论 0 1
  • 渐变的面目拼图要我怎么拼? 我是疲乏了还是投降了? 不是不允许自己坠落, 我没有滴水不进的保护膜。 就是害怕变得面...
    闷热当乘凉阅读 4,233评论 0 13