javascript – 有没有办法短路异步/等待流?
作者:互联网
async function update() {
var urls = await getCdnUrls();
var metadata = await fetchMetaData(urls);
var content = await fetchContent(metadata);
await render(content);
return;
}
//All the four functions return a promise. (getCdnUrls, fetchMetaData, fetchContent, render)
如果我们想在任何时候从外部中止序列怎么办?
比如,当执行fetchMetaData时,我们意识到不再需要渲染组件,我们想要取消剩余的操作(fetchContent和render).
消费者有没有办法从外面中止/取消?
我们可以在每次等待一个条件后检查,但这似乎是一种不太优雅的方式.它仍然会等待当前的操作完成.
解决方法:
我刚刚谈了这个 – 这是一个可爱的主题,但遗憾的是你并不会真正喜欢我将要提出的解决方案,因为它们是网关解决方案.
规范对你有什么用
取消“恰到好处”实际上非常困难.人们一直在研究这个问题,并决定不阻止异步功能.
尝试在ECMAScript核心中解决此问题有两个提议:
> Cancellation tokens – 增加了旨在解决此问题的取消令牌.
> Cancelable promise – 添加了catch cancel(e){syntax和throw.cancel语法,旨在解决此问题.
这两个提案在过去一周都发生了很大变化,所以我不会指望在明年左右到达.这些建议有点互补,并不存在争议.
您可以做些什么来解决这个问题
取消令牌很容易实现.可悲的是,你真正想要的那种取消(又名“third state取消,取消也不例外)”目前不能用异步功能,因为你不能控制它们的运行方式.你可以做两件事:
>使用协同代码 – bluebird使用您可以使用的生成器和承诺发出声音取消.
>使用流产语义实现令牌 – 这实际上非常简单,所以让我们在这里做
CancellationTokens
好吧,令牌信号取消:
class Token {
constructor(fn) {
this.isCancellationRequested = false;
this.onCancelled = []; // actions to execute when cancelled
this.onCancelled.push(() => this.isCancellationRequested = true);
// expose a promise to the outside
this.promise = new Promise(resolve => this.onCancelled.push(resolve));
// let the user add handlers
fn(f => this.onCancelled.push(f));
}
cancel() { this.onCancelled.forEach(x => x); }
}
这可以让你做类似的事情:
async function update(token) {
if(token.isCancellationRequested) return;
var urls = await getCdnUrls();
if(token.isCancellationRequested) return;
var metadata = await fetchMetaData(urls);
if(token.isCancellationRequested) return;
var content = await fetchContent(metadata);
if(token.isCancellationRequested) return;
await render(content);
return;
}
var token = new Token(); // don't ned any special handling here
update(token);
// ...
if(updateNotNeeded) token.cancel(); // will abort asynchronous actions
这是一种非常丑陋的方式,最佳的是你希望异步函数能够意识到这一点,但它们还没有(还).
最理想的是,你所有的临时函数都会注意到并且会抛出取消(再次,因为我们不能拥有第三个状态),它们看起来像:
async function update(token) {
var urls = await getCdnUrls(token);
var metadata = await fetchMetaData(urls, token);
var content = await fetchContent(metadata, token);
await render(content, token);
return;
}
由于我们的每个函数都可以识别取消,因此它们可以执行实际的逻辑取消 – getCdnUrls可以中止请求并抛出,fetchMetaData可以中止底层请求并抛出等等.
以下是在浏览器中使用XMLHttpRequest API编写getCdnUrl(注意单数)的方法:
function getCdnUrl(url, token) {
var xhr = new XMLHttpRequest();
xhr.open("GET", url);
var p = new Promise((resolve, reject) => {
xhr.onload = () => resolve(xhr);
xhr.onerror = e => reject(new Error(e));
token.promise.then(x => {
try { xhr.abort(); } catch(e) {}; // ignore abort errors
reject(new Error("cancelled"));
});
});
xhr.send();
return p;
}
这与没有协同程序的异步函数一样接近.它不是很漂亮但它确实可用.
请注意,您希望避免将取消视为异常.这意味着如果你的函数抛出取消,你需要在全局错误处理程序process.on(“unhandledRejection”,e => ……等)上过滤这些错误.
标签:cancellation,ecmascript-next,javascript,async-await,promise 来源: https://codeday.me/bug/20191004/1851568.html