有没有办法短路异步/等待流量? [英] Is there a way to short circuit async/await flow?

查看:89
本文介绍了有没有办法短路异步/等待流量?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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)

如果我们想要从外部中止序列,任何时候?

What if we want to abort the sequence from outside, at any time ?

说,当执行fetchMetaData时,我们意识到不再需要渲染组件,我们想要取消剩余的操作(fetchContent和render) 。
有没有办法让消费者从外面中止/取消?

Say, when fetchMetaData is being executed, we realize the component is no longer needed to be rendered and we want to cancel the remaining operations (fetchContent and render). Is there a way to abort/cancel from outside by consumer ?

我们可以在每次等待条件后检查,但这似乎是一种不太优雅的方式。并且它仍然会等待当前的操作完成。

We could check after each await for a condition, but that just seems an inelegant way of doing this. and it still will wait for the current operation to finish.

推荐答案

我刚刚谈了这个 - 这是一个可爱的话题但遗憾的是,你并不真的会喜欢我要提出的解决方案,因为它们是网关解决方案。

I just gave a talk about this - this is a lovely topic but sadly you're not really going to like the solutions I'm going to propose as they're gateway-solutions.

取消恰到好处实际上非常困难。人们一直在研究这个问题,并决定不阻止异步功能。

Getting cancellation "just right" is actually very hard. People have been working on just that for a while and it was decided not to block async functions on it.

有两个提案试图在ECMAScript核心中解决这个问题:

There are two proposals attempting to solve this in ECMAScript core:


  • < a href =https://github.com/zenparsing/es-cancel-token =noreferrer>取消令牌 - 添加了旨在解决此问题的取消令牌。

  • 可取消承诺 - 添加捕获取消(e) {语法和 throw.cancel 语法旨在解决此问题。

  • Cancellation tokens - which adds cancellation tokens that aim to solve this issue.
  • Cancelable promise - which adds catch cancel (e) { syntax and throw.cancel syntax which aims to address this issue.

这两个提案在上周中发生了重大变化所以我不会指望在明年左右到达。这些建议有点免费,并且没有争议。

Both proposals changed substantially over the last week so I wouldn't count on either to arrive in the next year or so. The proposals are somewhat complimentary and are not at odds.

取消令牌很容易实现。可悲的是,你 想要取消的那种取消(又名第三个状态取消,取消不是例外)目前不能使用异步功能,因为你无法控制它们的运行方式。你可以做两件事:

Cancellation tokens are easy to implement. Sadly the sort of cancellation you'd really want (aka "third state cancellation where cancellation is not an exception) is impossible with async functions at the moment since you don't control how they're run. You can do two things:


  • 使用协程 - bluebird 附带声音取消您可以使用的生成器和承诺。

  • 使用失败的语义实现令牌 - 这实际上非常简单,所以让我们在这里做。

  • Use coroutines instead - bluebird ships with sound cancellation using generators and promises which you can use.
  • Implement tokens with abortive semantics - this is actually pretty easy so let's do it here

好了,令牌信号取消:

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); }
}

这可以让你做类似的事情:

This would let you do something like:

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函数要注意这一点,但它们不是(尚未)。

Which is a really ugly way that would work, optimally you'd want async functions to be aware of this but they're not (yet).

最理想的是,你所有的临时功能都会知道,并且在取消时会抛出(同样,只因为我们可以'有第三状态)看起来像:

Optimally, all your interim functions would be aware and would throw on cancellation (again, only because we can't have third-state) which would look like:

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 可以中止基础请求并抛出等等。

Since each of our functions are cancellation aware, they can perform actual logical cancellation - getCdnUrls can abort the request and throw, fetchMetaData can abort the underlying request and throw and so on.

以下是使用 XMLHttpRequest getCdnUrl (注意单数)的方法c>浏览器中的API:

Here is how one might write getCdnUrl (note the singular) using the XMLHttpRequest API in browsers:

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;
}

这与没有协同程序的异步函数一样接近。它不是很漂亮,但肯定可以使用。

This is as close as we can get with async functions without coroutines. It's not very pretty but it's certainly usable.

请注意,您希望避免取消被视为异常。这意味着如果您的函数抛出取消,您需要在全局错误处理程序上过滤这些错误 process.on(unhandledRejection,e => ; ... 等。

Note that you'd want to avoid cancellations being treated as exceptions. This means that if your functions throw on cancellation you need to filter those errors on the global error handlers process.on("unhandledRejection", e => ... and such.

这篇关于有没有办法短路异步/等待流量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆