rwson

rwson

一个前端开发

fetch中的Timeout

随着ES6的普及,fetch已经渐渐取代了普通ajax在异步请求中的位置,以前在用jQuery中的ajax时,可以指定一个timeout属性,设置该请求的超时时间,但是原生的fetch并不支持该属性,如果遇到遇到一个请求一直pedding状态,就只能干等着,什么也干不了。由于fetch本身在被初始化后返回一个Promise对象,我们就可以对其再包一层Promise来实现在fetch请求中加入timeout的功能。

先来看下fetch的基本用法:

fetch("some url", {
    "method": "POST",
    "body": JSON.stringify({
        "key": "value",
        "arr": [1,2,3]
    }),
    ...
})
//  parse response to JSON object
.then((res) => res.json())
.then((res) => {
    //   success dome something
})
.catch((ex) => {
    //  exception occurded
});

在Promise中有个静态方法,叫Promise.race(),该方法接受一个数组作为参数,数组中的每个元素都是一个Promise的实例,大致用法如下:

let promise1 = new Promise((resoleve, reject) => {
    //  do something
});

let promise2 = new Promise((resoleve, reject) => {
    //  do something
});

//  ...

let race = Promise.race([promise1, promise2, ...]);

它返回一个Promise,该Promise根据传入的Promise的第一个完成状态(resolve/reject),只要该Promise已完成,就将其返回。

看个下面的例子:

const promise1 = new Promise((resolve, reject) => {
    //  1s后执行reject
    setTimeout(() => {
        reject("reject");
    }, 1000);
});

const promise2 = new Promise((resolve, reject) => {
    //  2s后执行resolve
    setTimeout(() => {
        resolve("success");
    }, 2000);
});

const racedPromise = Promise.race([promise1, promise2]);

racedPromise()
.then(() => {
    alert("success");
})
.catch(() => {
    alert("fail");
});

// 显然上面的代码最终会弹出fail,因为promise1的状态比promise2先确定,所以Promise.race最终返回的是Promise1,由于状态是reject,所以走到了catch回调。

上面说了好多废话:

下面实现一个可设置timeout的fetch:

/**
  * @param url      请求地址
  * @param opt      配置参数
  * @param timeout  
  **/
function abortFetch(url, opt, timeout) {
    if(typeof url !== "string") {
    }
    const fetchIns = fetch(url, opt || {});
    const abortIns = new Promise((resolve, reject) => {
        if(typeof timeout === "number" && timeout > 0) {
            setTimeout(() => {
                reject({
                    "type": "abord"
                });
            }, timeout);
        }
    });
    const finalPromise = Promise.race([fetchIns, abortIns]);
    return finalPromise;
}

//  用法

fetch("/fetch-timeout", {
    "method": "POST"
})
.then((res) => res.json())
.then((res) => {
    if(res.status >= 200 && res.status < 300 || res.status === 302) {
        //  do something response success
    } else {
        //  do something response failed
    }
})
.catch((ex) => {
    switch (ex.type) {
        case "abord": 
            //  do something when request abord
        break;
        
            ...
        
        default:
            //  ...
        break;
    }
});

到这里,就利用Promise.race实现了一个可配置超时时间的fetch