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).
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.*certain DOM APIs due violate this contract, notably window.open() on firefox pauses everything but setTimeout.
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
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.
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)
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.
> 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.
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.
> 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
const json = await (await fetch(https://api.github.com/orgs/facebook')).json();
or do I have to name it? const json = await fetch('https://api.github.com/orgs/facebook').then(res => res.json());EDIT: I missed the `.json()` part, but I suppose it doesn't return a Promise, no?
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...
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.
const responsePromise = fetch(https://api.github.com/orgs/facebook');
const response = await responsePromise;
const jsonPromise = response.json();
const json = await jsonPromise;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.
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.
You can do it, it's not insane, but native async/await will be much, much nicer.
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.
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.
const val = 'a'; val = 'b'; // TypeError
const obj = { val: 'a' }; obj.val = 'b'; // This line will still work.
obj = { val: 'b' }; // TypeError
There's also `Object.defineProperty()`, `Object.freeze()` for more control over mutability.
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) {}
`const fn = (i) => ...`
1. Admittedly my only data point is AirBnB's style guide / ESLint config: https://github.com/airbnb/javascript#references--prefer-cons...
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.
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.
Getting data is as easy as:
So I am quite happy when this lands in modern browsers asap.