在使用 fetch 的情况下怎么靠谱的去轮询接口呢🤔

1,873 阅读3分钟

小知识,大挑战!本文正在参与「程序员必备小知识」创作活动

本文已参与 「掘力星计划」 ,赢取创作大礼包,挑战创作激励金。

jquery, axios, fetch 的区别

jquery ajax: 是对原生 XHR 的封装,除此以外还增添了对 JSONP 的支持。但为了这个功能需要引入一个庞大的 jq 库,并且,对异步的支持不是太友好

axios ajax: 一个基于 Promise 用于浏览器和 nodejs 的 HTTP 客户端,本质上也是对原生 XHR 的封装,只不过它是 Promise 的实现版本,符合最新的ES规范,也是现在主流使用的 ajax 库

fetch: 使用了ES6中的 Promisse 对象。但是,fetch不是ajax的进一步封装,而是原生js,没有使用XMLHttpRequest对象。

总结起来:jquery ajax 建议放弃。如果正经的项目开发,建议上 axios。要是不正经的,或者是小 demo,直接用原生的 fetch 梭哈

场景假设

我们需要去轮询一个接口,同时对返回的数据做一个处理,我们可以借助 setInterval

大致的代码会如下:

let timeId = setInterval(() => {
    fetch(url, config)
}, time)

这种方式存在一个问题,fetch 中的操作,是异步的,假定执行完成后得到结果需要 2s,但轮询间隔为 1s,表现效果就是以为会有间隔时间,但实际是连着一直在做,由于消化不了,浏览器最后会被 setInterval 产生的任务撑到裂开

聊到 setInterval 就不得不提宏任务和微任务,宏任务指浏览器当前执行的主线任务,微任务就是支线任务,这是两个有序的队列。当在当前的时间节点,主线任务都搞定了,就会去拿一个支线任务来当主线来做,一直循环直到两条线的任务都搞定。

setInterval,负责定时产生任务,然后往支线队列扔,这只管产生,不管执行。至于什么时候做,则需要浏览器的线程来调度。调度的依据就是上一段所说

总结起来,setInterval,传入的时间参数,并不是指多次任务执行期间的间隔,而是指下一次任务产生的间隔时间,什么时候执行这次任务还需要看系统当前的运行环境

改进

fetch 包裹一个名为 call 的函数,在 fetch 执行的结果里,使用 setTimeout 去执行下一次 call,整个调用链有点类似于链表,只有等当前函数执行完成之后,才会去执行下一次

{
    call();
    function call(time = 1200) {
        fetch("https://cn.bing.com/hp/api/model", {
            "headers": {
                "accept": "*/*",
                "accept-language": "zh-CN,zh;q=0.9",
                "content-type": "application/json",
                "sec-ch-ua": "\"Chromium\";v=\"94\", \"Google Chrome\";v=\"94\", \";Not A Brand\";v=\"99\"",
            },
            "referrer": "https://cn.bing.com/",
            "referrerPolicy": "strict-origin-when-cross-origin",
        }).then(rep => rep.json()).then((data) => {
            setTimeout(() => call(time), time)
        });
    }
}

如果要根据条件来终止呢?

进一步的思考,以掘金的抽奖接口为例,我们手里有 1000矿,但实际抽奖的次数,不一定是 5次,终止的条件就变成了:一直抽奖,直到抽奖接口返回抽奖失败

思路为:我们引入一个标记 flag,定义 flag=ture 一直抽,flag=false 则结束抽奖

  1. call 中的第一行先判断
    1. flag,为 false 则终止,并执行统计函数,输出大伙的战果
    2. 如果是 true,则调用 fetch
  2. fetch 回调里使用 setTimeout 来定义下一次调用
  3. 根据此次的结果来判断是否需要调整 flagfalse

大致的代码如下:

function choujiang() {
    let nextLotteryFlag = true;
    asyncFetch(() => {})

    function asyncFetch(callback, time = 1200) {
        if (!nextLotteryFlag) {
            callback();
            return;
        };
        setTimeout(() => {
            fetch(`https://api.juejin.cn/growth_api/v1/lottery/draw?${userInfo}`, httpConfig)
                .then(resp => resp.json()).then(data => {
                    if (data.err_msg == 'success') {
                        console.log(name);
                    } else {
                        nextLotteryFlag = false;
                    }
                    asyncFetch(callback, time);
                })
        }, time)
    }
    return false;
}

原创不易,期待大家的鼓励❤~

image.png