乐趣区

关于promise:使用Promiserace实现超时机制取消XHR请求

咱们来看一下如何应用 Promise.race 来实现超时机制。
当然 XHR 有一个 timeout 属性,应用该属性也能够简略实现超时性能,然而为了能反对多个 XHR 同时超时或者其余性能,咱们采纳了容易了解的异步形式在 XHR 中通过超时来实现勾销正在进行中的操作。

让 Promise 期待指定工夫

首先咱们来看一下如何在 Promise 中实现超时。
所谓超时就是要在通过肯定工夫后进行某些操作,应用 setTimeout 的话很好了解。
首先咱们来串讲一个单纯的在 Promise 中调用 setTimeout 的函数。

//delayPromise.js
function delayPromise(ms) {return new Promise(function (resolve) {setTimeout(resolve, ms);
    });
}

Promise.race 中的超时

Promise.race 就是一组 promise 中只有有一个返回,其它的 promise 就不会执行后续回调(无论谬误还是胜利)

var winnerPromise = new Promise(function (resolve) {setTimeout(function () {console.log('this is winner');
            resolve('this is winner');
        }, 4);
    });
var loserPromise = new Promise(function (resolve) {setTimeout(function () {console.log('this is loser');
            resolve('this is loser');
        }, 1000);
    });
// 第一个 promise 变为 resolve 后程序进行,第二个 promise 不会进入回调
Promise.race([winnerPromise, loserPromise]).then(function (value) {console.log(value);    // => 'this is winner'
});

咱们能够将方才的 delayPromise 和其它 promise 对象一起放到 Promise.race 中来是实现简略的超时机制。

//simple-timeout-promise.js
function delayPromise(ms) {return new Promise(function (resolve) {setTimeout(resolve, ms);
    });
}
function timeoutPromise(promise, ms) {var timeout = delayPromise(ms).then(function () {throw new Error('Operation timed out after' + ms + 'ms');
        });
    return Promise.race([promise, timeout]);// 返回 taskPromise 和 timeoutPromise 组成的 Promise.race
}

定制 TimeoutError 对象

//TimeoutError
class TimeoutError extends Error{constructor(msg){super(msg)
  }
}

实例

class TimeoutError extends Error{constructor(msg){super(msg)
  }
}
function delayPromise(ms) {return new Promise(function (resolve) {setTimeout(resolve, ms);
    });
}
function timeoutPromise(promise, ms) {var timeout = delayPromise(ms).then(function () {throw new TimeOutError('Operation timed out after' + ms + 'ms');
        });
    return Promise.race([promise, timeout]);
}
// 运行示例
var taskPromise = new Promise(function(resolve){
    // 轻易一些什么解决
    var delay = Math.random() * 2000;
    setTimeout(function(){resolve(delay + "ms");
    }, delay);
});
timeoutPromise(taskPromise, 1000).then(function(value){console.log("taskPromise 在规定工夫内完结 :" + value);
}).catch(function(error){if(error instance of TimeoutError){console.log("产生超时", error);
    }   
});

通过超时勾销 XHR 操作

到这里,我想各位读者都曾经对如何应用 Promise 来勾销一个 XHR 申请都有一些思路了吧。

勾销 XHR 操作自身的话并不难,只须要调用 XMLHttpRequest 对象的 abort() 办法就能够了。

为了能在内部调用 abort() 办法,咱们先对之前本节呈现的 getURL 进行简略的扩大,cancelableXHR 办法除了返回一个包装了 XHR 的 promise 对象之外,还返回了一个用于勾销该 XHR 申请的 abort 办法。

//delay-race-cancel.js
function cancelableXHR(URL) {var req = new XMLHttpRequest();
    var promise = new Promise(function (resolve, reject) {req.open('GET', URL, true);
            req.onload = function () {if (req.status === 200) {resolve(req.responseText);
                } else {reject(new Error(req.statusText));
                }
            };
            req.onerror = function () {reject(new Error(req.statusText));
            };
            req.onabort = function () {reject(new Error('abort this request'));
            };
            req.send();});
    var abort = function () {
        // 如果 request 还没有完结的话就执行 abort
        // https://developer.mozilla.org/en/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest
        if (req.readyState !== XMLHttpRequest.UNSENT) {req.abort();
        }
    };
    return {
        promise: promise,
        abort: abort
    };
}

在这些问题都明了之后,剩下只须要进行 Promise 解决的流程进行编码即可。大体的流程就像上面这样。

通过 cancelableXHR 办法获得包装了 XHR 的 promise 对象和勾销该 XHR 申请的办法

在 timeoutPromise 办法中通过 Promise.race 让 XHR 的包装 promise 和超时用 promise 进行竞争。

XHR 在超时前返回后果的话

和失常的 promise 一样,通过 then 返回申请后果
产生超时的时候

抛出 throw TimeoutError 异样并被 catch

catch 的谬误对象如果是 TimeoutError 类型的话,则调用 abort 办法勾销 XHR 申请

将下面的步骤总结一下的话,代码如下所示。

//delay-race-cancel-play.js
class TimeoutError extends Error{constructor(msg){super(msg)
  }
}
function delayPromise(ms) {return new Promise(function (resolve) {setTimeout(resolve, ms);
    });
}
function timeoutPromise(promise, ms) {var timeout = delayPromise(ms).then(function () {return Promise.reject(new TimeoutError('Operation timed out after' + ms + 'ms'));
        });
    return Promise.race([promise, timeout]);
}
function cancelableXHR(URL) {var req = new XMLHttpRequest();
    var promise = new Promise(function (resolve, reject) {req.open('GET', URL, true);
            req.onload = function () {if (req.status === 200) {resolve(req.responseText);
                } else {reject(new Error(req.statusText));
                }
            };
            req.onerror = function () {reject(new Error(req.statusText));
            };
            req.onabort = function () {reject(new Error('abort this request'));
            };
            req.send();});
    var abort = function () {
        // 如果 request 还没有完结的话就执行 abort
        // https://developer.mozilla.org/en/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest
        if (req.readyState !== XMLHttpRequest.UNSENT) {req.abort();
        }
    };
    return {
        promise: promise,
        abort: abort
    };
}
var object = cancelableXHR('http://httpbin.org/get');
// main
timeoutPromise(object.promise, 1000).then(function (contents) {console.log('Contents', contents);
}).catch(function (error) {if (error instanceof TimeoutError) {object.abort();
        return console.log(error);
    }
    console.log('XHR Error :', error);
});

下面的代码就通过在肯定的工夫内变为解决状态的 promise 对象实现了超时解决。

退出移动版