在现代 JavaScript 中 - 不
承诺已经解决(哈),而且似乎永远不可能取消(待定)承诺。
相反,有一个跨平台(节点、浏览器等)取消原语,作为 WHATWG(也构建 HTML 的标准机构)的一部分,称为AbortController
。您可以使用它来取消功能返回承诺而不是承诺本身:
// Take a signal parameter in the function that needs cancellation
async function somethingIWantToCancel({ signal } = {}) {
// either pass it directly to APIs that support it
// (fetch and most Node APIs do)
const response = await fetch('.../', { signal });
// return response.json;
// or if the API does not already support it -
// manually adapt your code to support signals:
const onAbort = (e) => {
// run any code relating to aborting here
};
signal.addEventListener('abort', onAbort, { once: true });
// and be sure to clean it up when the action you are performing
// is finished to avoid a leak
// ... sometime later ...
signal.removeEventListener('abort', onAbort);
}
// Usage
const ac = new AbortController();
setTimeout(() => ac.abort(), 1000); // give it a 1s timeout
try {
await somethingIWantToCancel({ signal: ac.signal });
} catch (e) {
if (e.name === 'AbortError') {
// deal with cancellation in caller, or ignore
} else {
throw e; // don't swallow errors :)
}
}
不,我们还不能这么做。
ES6 Promise 不支持取消yet。它正在路上,它的设计是很多人都努力工作的。Sound取消语义很难正确,而且这项工作正在进行中。关于“fetch”存储库、esdiscuss 和 GH 上的其他几个存储库存在有趣的争论,但如果我是你,我会耐心等待。
但是,但是,但是..取消真的很重要!
是的,事实是取消是really客户端编程中的一个重要场景。您所描述的情况(例如中止网络请求)很重要,而且无处不在。
所以……语言把我搞砸了!
是的,对此感到抱歉。在指定进一步的事情之前,必须先承诺 - 所以他们没有一些有用的东西,比如.finally
and .cancel
- 不过,它正在通过 DOM 达到规范。取消是not事后想来,这只是时间限制和 API 设计的迭代方法。
那我能做什么呢?
您有多种选择:
- 使用第三方库,例如bluebird https://github.com/petkaantonov/bluebird他们的行动速度比规范快得多,因此可以取消以及其他一些好处 - 这就是像 WhatsApp 这样的大公司所做的事情。
- 通过取消token.
使用第三方库是非常明显的。至于令牌,您可以让您的方法接受一个函数,然后调用它,如下所示:
function getWithCancel(url, token) { // the token is for cancellation
var xhr = new XMLHttpRequest;
xhr.open("GET", url);
return new Promise(function(resolve, reject) {
xhr.onload = function() { resolve(xhr.responseText); });
token.cancel = function() { // SPECIFY CANCELLATION
xhr.abort(); // abort request
reject(new Error("Cancelled")); // reject the promise
};
xhr.onerror = reject;
});
};
这会让你做:
var token = {};
var promise = getWithCancel("/someUrl", token);
// later we want to abort the promise:
token.cancel();
您的实际用例 -last
对于令牌方法来说这并不太难:
function last(fn) {
var lastToken = { cancel: function(){} }; // start with no op
return function() {
lastToken.cancel();
var args = Array.prototype.slice.call(arguments);
args.push(lastToken);
return fn.apply(this, args);
};
}
这会让你做:
var synced = last(getWithCancel);
synced("/url1?q=a"); // this will get canceled
synced("/url1?q=ab"); // this will get canceled too
synced("/url1?q=abc"); // this will get canceled too
synced("/url1?q=abcd").then(function() {
// only this will run
});
不,像 Bacon 和 Rx 这样的库在这里并不“闪耀”,因为它们是可观察的库,它们只是具有与用户级承诺库相同的优势,不受规范约束。我想我们会等着看 ES2016 中可观察对象何时原生化。他们are不过对于提前输入来说很漂亮。