News for hackers. Stuff that matters.
Re:JavaScript async/await implemented in V8 by tanto on Monday May 16, 2016 @09:22PM (#11710773)
The moment I started using async await (with babel) combined with the new fetch API so many libraries got obsolete.

Getting data is as easy as:

  async function main () {
    try {
      const res = await fetch('https://api.github.com/orgs/facebook');
      const json = await res.json();
      console.log(json);
    } catch (e) {
      // handle error
    }  
  }
So I am quite happy when this lands in modern browsers asap.
Re:JavaScript async/await implemented in V8 by tomp on Tuesday May 17, 2016 @04:28AM (#11712040)
Can anyone point to a good explanation why this is preferable to cooperative threads (coroutines)? I.e. the above code could easily be written as

  function main() {
    try {
      const res = fetch('https://api.github.com/orgs/facebook');    // yield here
      const json = res.json();                                      // yield here
      console.log(json);
    } catch (e) {
      // handle error
    }
  }
and the runtime would automatically yield this coroutine and let other coroutines run whenever some call blocks.

I guess the only realy difference would be that I would make `await` (and `async`) implicit, similar to how exceptions are handled implicitly in the above snippet (i.e. we don't annotate functions as `throwing` and we don't use an `attempt` statement (analogous to `await`) when calling `throwing` functions).

Edit: Reading various articles, the best explanation is that since the single-threaded nature of JavaScript makes synchronisity implicit (i.e. all code is run in an implicit transaction), it has to make asynchronisity explicit. The alternative would be to have coroutines/fibres (implicit asynchronisity), but with explicit `atomic` blocks (for synchronisity).

C# doesn't provide any synchronisity guarantees, so I guess the motivations there were different (it's really hard (impossible?) to implement fibres efficiently, and even harder to allow for native code interoperability).

Re:JavaScript async/await implemented in V8 by xg15 on Tuesday May 17, 2016 @07:24AM (#11712654)
There is also the the point of flexibility. Async/await works on promises, however promises themself are just perfectly un-magical JS objects that accept a callback. This results in two effects:

1) Yielding is decoupled from the asynchronous action itself, giving the caller fine-grained control when to yield. For example, with async/await, management of parallel operations is straight-forward:

  var promise = fetch('https://foo.com/');
  // ... do something useful while the fetch runs ...
  var result = yield promise;
Same goes for concurrent fetches:

  var promise1 = fetch('https://foo.com/');
  var promise2 = fetch('https://bar.com/');
  [result1, result2] = yield Promise.all(promise1, promise2 );
2) Promises + async/await allow you to switch back-and-forth very easily between callback-style and coroutine-style APIs, which makes integrating old APIs very easy:

  async function f() {
   var p = fetch('https://foo.com/');
   p = p.then(function(inbetweenResult) {return new Promise(function(resolve) {
    oldapi.onresult = resolve;
    oldapi.process(inbetweenResult);
   })});
   return await p;
  }

  f().then(...) 
... and so on.
Re:JavaScript async/await implemented in V8 by cwmma on Tuesday May 17, 2016 @09:38AM (#11713494)
The explicitness is the reason currently you can assume* that if nobody will ever modify variables unless you explicitly give up control somehow, if you were to have implicit await then that would no longer be the case and any function call could theoretically pause the function and give up control to other functions.

*certain DOM APIs due violate this contract, notably window.open() on firefox pauses everything but setTimeout.

Re:JavaScript async/await implemented in V8 by olau on Tuesday May 17, 2016 @11:51AM (#11714615)
Just to expand a bit upon this: That you can assume a single thread simplifies the code immensely, e.g. when modifying state. It's important to be explicit about when this assumption is broken.
Re:JavaScript async/await implemented in V8 by gpderetta on Tuesday May 17, 2016 @09:45AM (#11713547)
The problem with that is that any mutable escaped variable (i.e. explicitly or implicitly global) can be potentially modified by any function call. On the other hand a non escaped variable cannot be mutated behind your back, yield or no yield. At least, that's the case in a sane language, not sure about JS.
Re:JavaScript async/await implemented in V8 by cwmma on Tuesday May 17, 2016 @09:54AM (#11713611)
escaped/non-escaped variables are not a thing in JS
Re:JavaScript async/await implemented in V8 by gpderetta on Tuesday May 17, 2016 @09:58AM (#11713647)
There are no locals in JS (honest question)? If that the case you have to assume that everything can be mutated everywhere unless for those functions that explicitly and fully document their side effects (and yield would certainly be a side effect).
Re:JavaScript async/await implemented in V8 by cwmma on Tuesday May 17, 2016 @10:46AM (#11714069)
variables declared with the var keyword are functionally scoped in JS so they are local to the function they are declared in and are lexically scoped to the closure of that function. So your local variables you can be fairly confident are not mutated anywhere else.

That being said you can totally have globals and whatnot that can be mutated anywhere (e.g. parameters passed to a function or literal globals installed in the global scope) or things declared at a lower scope that can still be modified by multiple functions.

Hope that clears it up

Re:JavaScript async/await implemented in V8 by amelius on Tuesday May 17, 2016 @05:18AM (#11712201)
Perhaps this is the reason:

Is it possible to have a yield inside a called function (i.e., not the generator function itself)? Last time I checked this was not allowed (?)

Anyway, if so, I would strongly prefer this over a async/await construct, which is less general.

Re:JavaScript async/await implemented in V8 by masklinn on Tuesday May 17, 2016 @07:23AM (#11712644)
> Is it possible to have a yield inside a called function (i.e., not the generator function itself)?

Call a generator function from a generator function? Sure, you just need to transitively yield its content using `yield* [[expr]]` (which delegates part of the iteration to the `expr` generator)

Re:JavaScript async/await implemented in V8 by amelius on Tuesday May 17, 2016 @07:27AM (#11712664)
So what tomp is doing:

    const res = fetch('https://api.github.com/orgs/facebook');    // yield here
actually requires a "yield" in front of the "fetch"? But what if I want the fetch() function to decide whether to yield or not?

Of course, fetch(), could yield a status specifying whether its calling-ancestors should yield, but this can become unwieldy very quickly, and might require an exception mechanism of its own. Better to just let called functions yield.

Re:JavaScript async/await implemented in V8 by masklinn on Tuesday May 17, 2016 @08:11AM (#11712899)
In javascript it would yes (actually a `yield*`, or an `await` in async/await). With native coroutines it wouldn't (the runtime would implicitly yield on IO).

> But what if I want the fetch() function to decide whether to yield or not?

In javascript? The question doesn't really make sense, a function is either sync or async.

Re:JavaScript async/await implemented in V8 by amelius on Tuesday May 17, 2016 @10:08AM (#11713730)
> The question doesn't really make sense, a function is either sync or async.

Well, either fetch() blocks, or not. If it blocks, then it should yield, its caller should yield, etc. If it doesn't block, then it can just run to completion.

Re:JavaScript async/await implemented in V8 by masklinn on Tuesday May 17, 2016 @10:23AM (#11713872)
Don't elide important parts of the quote. In javascript your question doesn't make sense.

> Well, either fetch() blocks, or not.

Javascript functions always block.

> If it blocks, then it should yield, its caller should yield, etc. If it doesn't block, then it can just run to completion.

If a function is async, you must yield[0]. It may not need* that capability (conditionally) and doesn't have to yield at all e.g.

    function*() {
        return 4;
    }
is valid and will not suspend. You must still yield*[0] on it so that it is properly resolved as a generator.

[0] I think that's not an issue for async/await as it's syntactic sugar for then chains, the code itself may be converted to a generator or whatever but it will embed the driver

Re:JavaScript async/await implemented in V8 by a-saleh on Tuesday May 17, 2016 @06:22AM (#11712414)
Is this the problem Python has solved with 'yield from' construct?
Re:JavaScript async/await implemented in V8 by neoeldex on Tuesday May 17, 2016 @08:11AM (#11712901)
Its basically the same thing. But javascript doesn't come with a new eventloop and other construct to use it, since similar functionality is already in js
Re:JavaScript async/await implemented in V8 by totemizer on Tuesday May 17, 2016 @06:49AM (#11712507)
Probably not preferable. Async await works on top of promises...
Re:JavaScript async/await implemented in V8 by turblety on Tuesday May 17, 2016 @04:46AM (#11712083)
It might be something to do with backwards compatibility.
Re:JavaScript async/await implemented in V8 by tomp on Tuesday May 17, 2016 @04:59AM (#11712135)
What do you mean? Edit: I mean, can you give any examples of how old code would be broken by implementing something like this?
Re:JavaScript async/await implemented in V8 by startling on Monday May 16, 2016 @09:28PM (#11710797)
Is the syntax composable? Can I do

    const json = await (await fetch(https://api.github.com/orgs/facebook')).json();
or do I have to name it?
Re:JavaScript async/await implemented in V8 by bsou on Monday May 16, 2016 @09:29PM (#11710801)
yeah that works, your statement is fine
Re:JavaScript async/await implemented in V8 by startling on Monday May 16, 2016 @09:30PM (#11710804)
nice, thanks!
Re:JavaScript async/await implemented in V8 by dvlsg on Monday May 16, 2016 @10:07PM (#11710940)
You probably can, but you may as well just do this instead:

    const json = await fetch('https://api.github.com/orgs/facebook').then(res => res.json());
Re:JavaScript async/await implemented in V8 by ludamad on Tuesday May 17, 2016 @12:18AM (#11711321)
I think the await code is more explicit, whereas a callback is somewhat ambiguous
Re:JavaScript async/await implemented in V8 by nathancahill on Tuesday May 17, 2016 @12:39AM (#11711376)
I'll be glad to do away with callbacks completely tbh.
Re:JavaScript async/await implemented in V8 by smilekzs on Monday May 16, 2016 @09:32PM (#11710811)
What you asked for is whether `await` is idempotent. No it isn't. The "argument" to `await` is a Promise, and the "return value" of it is the resolved value of the Promise (i.e. `Promise#then`).

EDIT: I missed the `.json()` part, but I suppose it doesn't return a Promise, no?

Re:JavaScript async/await implemented in V8 by tanto on Monday May 16, 2016 @09:38PM (#11710838)
actually .json() returns a Promise. The first fetch only returns HTTP status code and some other details. The content itself is streamed. You can also access the stream if you want too. In general you would rather just call .json() and get the parsed data.

The fetch function can for example also be used to get images. There you would use .blob() and assign that to an image.src with URL.createObjectURL(blob);

See here: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/U...

Re:JavaScript async/await implemented in V8 by startling on Tuesday May 17, 2016 @01:40PM (#11715614)
This is totally not what I'm asking, thanks.
Re:JavaScript async/await implemented in V8 by tigershark on Tuesday May 17, 2016 @04:04AM (#11711969)
I don't understand your code. the .json() should be inside the parentheses because you cannot await something that has already been converted from a Promise to a real value. And even in that case that code will be exactly equivalent to the synchronous code because you are awaiting in place the result of the Promise. So, given my very limited knowledge of javascript, I think that probably you can write it moving the .json() inside, but I don't see why you would ever want to do it if you can have the same result without the await boilerplate...

EDIT: got it, you want to do it inside an aync function with some other code after the await, then I think it makes sense in that case.

Re:JavaScript async/await implemented in V8 by neoeldex on Tuesday May 17, 2016 @08:23AM (#11712973)
fetch() returns a promise, which is unwrapped using await. fetch's promise contains a response object, which has a json method, which returns a promise.

  const responsePromise = fetch(https://api.github.com/orgs/facebook');
  const response = await responsePromise;
  const jsonPromise = response.json();
  const json = await jsonPromise;
Re:JavaScript async/await implemented in V8 by nailer on Tuesday May 17, 2016 @04:53AM (#11712120)
Tip: if you use anything other than fetch you'll just get JSON back immediately based on the MIME type, without the unnecessary conversion step.
Re:JavaScript async/await implemented in V8 by tehbeard on Tuesday May 17, 2016 @05:50AM (#11712321)
Those other than fetch()s still do the "unnecessary conversion step". Don't dismiss fetch by the fact it doesn't hand hold or make assumptions.

It should be said that fetch() is quite low level call compared to what we've had before regarding ajax, so it makes sense in larger projects to wrap it to keep any logging, error handling and retry in one place.

Re:JavaScript async/await implemented in V8 by nailer on Tuesday May 17, 2016 @05:54AM (#11712335)
> it doesn't hand hold or make assumptions

Using MIME types isn't hand holding or making an assumption.

Yes, people who need the low level features will wrap it with a series of competing high level libraries. It's a missed opportunity to make a standard, capable high level API though.

Re:JavaScript async/await implemented in V8 by moron4hire on Tuesday May 17, 2016 @08:05AM (#11712865)
Fetch API also doesn't have any means of tracking progress, making it strictly inferior to XmlHTTPRequest
Re:JavaScript async/await implemented in V8 by nailer on Tuesday May 17, 2016 @09:10AM (#11713277)
In fetch's defence it will in future once the Streams API is finalised: https://github.com/whatwg/fetch/issues/21
Re:JavaScript async/await implemented in V8 by neoeldex on Tuesday May 17, 2016 @08:16AM (#11712935)
Fetch does have streaming uploads and downloads, and a quite some other features which doesn't come with xmlHttpRequest. Making it not strictly inferior to XMLHttpRequest
Re:JavaScript async/await implemented in V8 by moron4hire on Tuesday May 17, 2016 @10:08AM (#11713733)
Will have. Future tense. Not present tense.
Re:JavaScript async/await implemented in V8 by mathias on Tuesday May 17, 2016 @03:17PM (#11716480)
Re:JavaScript async/await implemented in V8 by grandalf on Monday May 16, 2016 @11:38PM (#11711222)
yes code like that works.
Re:JavaScript async/await implemented in V8 by ludamad on Monday May 16, 2016 @09:32PM (#11710813)
With this natively in V8 the debug experience ought to be much better than now, too
Re:JavaScript async/await implemented in V8 by foota on Monday May 16, 2016 @09:33PM (#11710814)
Is using async/await through babel a good idea? When I last looked at it I thought it was using a busy loop which didn't seem like such a great idea to use in shipping code.
Re:JavaScript async/await implemented in V8 by nostrademons on Monday May 16, 2016 @09:56PM (#11710890)
It's okay but clumsy. It's a state machine, not a busy loop, but the compiled code is a lot more verbose than the input code (and so you pay a latency penalty on the client). The developer experience is also sub-par: you'll get regenerator stack frames in any of your stack traces, and it interacts poorly with babel-watch.

You can do it, it's not insane, but native async/await will be much, much nicer.

Re:JavaScript async/await implemented in V8 by tlrobinson on Tuesday May 17, 2016 @12:57AM (#11711428)
Can you elaborate on "it interacts poorly with babel-watch."?
Re:JavaScript async/await implemented in V8 by nostrademons on Tuesday May 17, 2016 @12:51PM (#11715138)
IIRC under babel-watch it required me to include babel-polyfill at the top of my main script, but under babel-node it prevented me from including babel-polyfill at the top of my main script. (babel-polyfill has a check so that it can only be required once, and errors out otherwise.) That meant I needed to keep commenting/uncommenting that line when I wanted to debug things.

There were also cases where I wasn't sure if babel-watch was reloading dependencies properly if I used async code in a file, which is why I had to keep switching to babel-node or running the code through babel and inspecting the generated code to debug. Overall, it just felt very new and unpolished (which I guess it was...I was working with it around March, so babel-watch was < 1 month old and babel async/await was about 2-3 months at the time) to be relying on all the time.

Re:JavaScript async/await implemented in V8 by hzoo on Monday May 16, 2016 @10:38PM (#11711056)
If you don't want to use regenerator and your environment supports generators already you can use the async-to-generator transform https://gist.github.com/rauchg/8199de60db48026a6670620a1c33b...
Re:JavaScript async/await implemented in V8 by tlrobinson on Monday May 16, 2016 @09:39PM (#11710841)
It's fine. It transforms async functions into a state machine.
Re:JavaScript async/await implemented in V8 by AgentME on Monday May 16, 2016 @10:38PM (#11711061)
Async/await is just syntax-sugar around Promises. No busy loops involved. (Those generally don't work in Javascript anyway.)
Re:JavaScript async/await implemented in V8 by BinaryIdiot on Tuesday May 17, 2016 @12:31AM (#11711354)
This overlooks Promises needing to be polyfilled. Though that's certainly not using busy loops either (though most of what I've seen overlooks more performant setTimeout(fn, 0) alternatives which is unfortunate).
Re:JavaScript async/await implemented in V8 by AgentME on Tuesday May 17, 2016 @05:35PM (#11717670)
Async/await already needs to be transpiled in for most browsers, so Promises needing to be polyfilled seems like the least of your problems.
Re:JavaScript async/await implemented in V8 by viperscape on Monday May 16, 2016 @09:58PM (#11710906)
The try and catch inside the async function is also great. Curious though, is using const over let common? I usually use const for imports and module level globals, and let inside functions. I recently got back into javascript, and it's nice to see it flourishing.
Re:JavaScript async/await implemented in V8 by bb85 on Monday May 16, 2016 @10:02PM (#11710919)
The great thing about let and const, for me, is that it gives you crucial information about variables without having to look further down.

const should be the default, and if you're going to reassign it, then use let.

In most code, you'll use const on the vast majority of variables, and it'll make let assignments stick out, which helps a lot when you're browsing the code or refactoring.

Re:JavaScript async/await implemented in V8 by spraak on Tuesday May 17, 2016 @04:15AM (#11711996)
This right here. One of my friends (a more experienced developer) told me at coffee one day, "I use const for everything" which really stuck with me. It really helps make it clear if you need a mutation to have let stand out.
Re:JavaScript async/await implemented in V8 by rpastuszak on Tuesday May 17, 2016 @05:42AM (#11712287)
However, it's worth noting that using const doesn't make variables immutable. It only protects them of being reassigned:

const val = 'a'; val = 'b'; // TypeError

const obj = { val: 'a' }; obj.val = 'b'; // This line will still work.

obj = { val: 'b' }; // TypeError

Re:JavaScript async/await implemented in V8 by dcherman on Tuesday May 17, 2016 @10:41AM (#11714027)
Yep. In those cases, I still prefer let personally even though I know the variable itself will not be re-assigned, kind of as an indicator that I expect the referenced object to be mutated.
Re:JavaScript async/await implemented in V8 by candu on Tuesday May 17, 2016 @11:18AM (#11714353)
Coming from a background that includes some experience with C / C++, `const` here works as I expect it to. I'd prefer `const` even for the object reference case: I get errors on reassignment this way, and presumably it makes additional optimizations possible. To each their own, I suppose :)

There's also `Object.defineProperty()`, `Object.freeze()` for more control over mutability.

Re:JavaScript async/await implemented in V8 by WorldMaker on Tuesday May 17, 2016 @11:55AM (#11714657)
I just wish let was in fact const (and something like "local" could be let), simply because the keyword let is nicer to type, easier to read, and more importantly better in line with the meaning of let in Lisp/Scheme/ML/OCaml/et al.
Re:JavaScript async/await implemented in V8 by spraak on Tuesday May 17, 2016 @02:07PM (#11715871)
Yeah, I was thinking the same! I was even considering writing a custom babel transform to do this for me :P
Re:JavaScript async/await implemented in V8 by bubersson on Monday May 16, 2016 @11:10PM (#11711155)
We also favor using const as a default for everything in our team's code. When someone uses let, it's right away obvious that the reference will change.

One case that surprised me a bit is that it's correct to use const in for-of loop, e.g.

for (const a of arr) {}

Re:JavaScript async/await implemented in V8 by spraak on Tuesday May 17, 2016 @04:18AM (#11712008)
I've been using a lot of const function declarations

`const fn = (i) => ...`

Re:JavaScript async/await implemented in V8 by tlrobinson on Tuesday May 17, 2016 @12:59AM (#11711435)
Yes, http://eslint.org/docs/rules/prefer-const is a fairly common [1] ESLint rule.

1. Admittedly my only data point is AirBnB's style guide / ESLint config: https://github.com/airbnb/javascript#references--prefer-cons...

Re:JavaScript async/await implemented in V8 by g_delgado14 on Monday May 16, 2016 @10:03PM (#11710926)
const is more declarative as you can immediately tell that the data isn't going to change throughout the duration of the program (although technically you can change the data, just not the reference).
Re:JavaScript async/await implemented in V8 by lewisl9029 on Monday May 16, 2016 @11:10PM (#11711154)
> although technically you can change the data, just not the reference

This is why I'm hoping to see native JS immutable data structures eventually (maybe it's already in a proposal somewhere?). ImmutableJS and Mori are great, but having a native solution that's available everywhere would be ideal.

Re:JavaScript async/await implemented in V8 by callahad on Tuesday May 17, 2016 @09:09AM (#11713270)
Consider looking at other languages. ClojureScript does a pretty good job at immutability, Elm is great for immutability + strong typing, and PureScript adds in Haskell's advanced type system (typeclasses, HKTs, etc.)
Re:JavaScript async/await implemented in V8 by lewisl9029 on Tuesday May 17, 2016 @03:05PM (#11716384)
Definitely. ClojureScript is what I usually choose for my own projects, but the reality of the front-end job market is that JavaScript still dominates, so improvements to JavaScript itself will still have a significant impact on the lives of developers everywhere regardless of the existence of other compile-to-JS languages.
Re:JavaScript async/await implemented in V8 by shimn on Wednesday May 18, 2016 @03:15PM (#11724884)
When I want to ensure strict immutability, I use an implementation of deepFreeze (https://github.com/jsdf/deep-freeze) that recursively calls Object.freeze() on all child properties. Obviously slower than strict browser support. Does ClojureScript rely on the Clojure compiler to ensure the properties aren't modified or is there some polyfill?
Re:JavaScript async/await implemented in V8 by WalterSear on Monday May 16, 2016 @10:10PM (#11710954)
IMHE, const is the generally the default and let is the exception.
Re:JavaScript async/await implemented in V8 by sotojuan on Monday May 16, 2016 @10:59PM (#11711128)
I'm alone in this but I prefer `let` over everything else unless it's something like Redux action strings, database connections strings, or anything of the like. I rarely use `const` for the ridiculous reason that I find it too long (5 characters!). `let` is short and reads nicely and my programming style in general never mutates stuff anyway (I use Ramda/always return new data).

That said, `const` is picking up to be the default for everything. I can definitely see good reasons to use it when declaring functions and required modules or working in a team.

Re:JavaScript async/await implemented in V8 by nemild on Monday May 16, 2016 @11:43PM (#11711234)
I'm curious why you let the amount of typing dictate what you use? If five characters is such an impediment, why not just use a snippet letting you have less error prone code than currently (e.g., 'co ' expands to const).
Re:JavaScript async/await implemented in V8 by garyclarke27 on Tuesday May 17, 2016 @02:56AM (#11711780)
As you admitted, your behaviour is rediculous, hope for your sake, your boss or customers don't read this.
Re:JavaScript async/await implemented in V8 by dfischer on Monday May 16, 2016 @10:56PM (#11711117)
What do you use for the new fetch api on node?
Re:JavaScript async/await implemented in V8 by pitaj on Monday May 16, 2016 @11:23PM (#11711184)
Re:JavaScript async/await implemented in V8 by mikeryan on Monday May 16, 2016 @11:09PM (#11711150)
I don't use it on Node but this syntax is great on react native.
Re:JavaScript async/await implemented in V8 by ascagnel_ on Tuesday May 17, 2016 @07:14AM (#11712600)
node-fetch is one option, but I go with isomorphic-fetch[0] (since it can be run client-side via Babel).

[0] https://github.com/matthew-andrews/isomorphic-fetch

Re:JavaScript async/await implemented in V8 by danielstocks on Tuesday May 17, 2016 @08:34AM (#11713037)
fyi isomorphic-fetch is just a universal wrapper around node-fetch and github-fetch
Re:JavaScript async/await implemented in V8 by Kiro on Tuesday May 17, 2016 @05:06AM (#11712161)
Why do you need to put await before res.json?
Re:JavaScript async/await implemented in V8 by tomelders on Tuesday May 17, 2016 @05:12AM (#11712178)
Someone correct me if I'm wrong here, this is just my hunch - but according to the fetch spec, res.json returns a promise, which are async. I'm assuming so you have the chance to handle errors as a result of malformed JSON.
Re:JavaScript async/await implemented in V8 by thomasahle on Tuesday May 17, 2016 @07:14AM (#11712597)
Promises are used for catching errors?
Re:JavaScript async/await implemented in V8 by onestone on Tuesday May 17, 2016 @07:48AM (#11712766)
Yes.
Re:JavaScript async/await implemented in V8 by jwmerrill on Tuesday May 17, 2016 @08:30AM (#11713010)
At the time that the request resolves, it has received and processed the response headers, but it may still be receiving the response body (which is represented as a stream). All the body representations (like .text() or .json()) need to wait until the body has arrived before they can resolve.

https://fetch.spec.whatwg.org/#concept-body-consume-body