It's called syntactic sugar because it doesn't introduce any substantial new feature to the language, it's just a sweeter syntax.
So yes: destructuring and += are examples of syntactic sugar because you could write it in the longer, less clean form, and it would be equivalent.
As a side note, sometimes the verb "to desugar" is used to mean "to turn into the equivalent syntax without sugar" As in this example sentence:
a += b desugars to a = a + b
Async/await is not an true example of syntactic sugar, it's an entire new feature in the language. You can re-write async/code to use then/catch handlers but it's much more than a simple syntax transformation, it's a re-write.
In conclusion, it's called "sugar" probably because it implies it's something you sprinkle onto the language syntax to make it easier, but not a whole new set of features. Under the hood, syntactic sugar could be implemented as a transformation of the syntax tree, so the runtime engine may not even need to know about it.
And I agree with the "diabetes" argument: too much syntactic sugar isn't good, it can definitely be taken too far when it makes code harder to read instead of easier to read.
In terms of async/await, and then/catch. When do you feel is a good time to use either?
For asynchronous operations, I have most often resort to fetch and then/catch. Not a very varied vocabulary.
@jenninat0r
, It might just be a typo, but I think you meant async/await syntax vs. promises which use then/catch syntax to chain operations (with .catch(cb) being used for error handling). Not try/catch.
In the case of async/await, you'd just use normal try/catch syntax around your await calls for error handling.
Both are used to accomplish the same thing, so I'd say which one you use is a matter of preference. Promises are very similar to monads (although technically they don't quite qualify as such), and I suspect that promises were developed to kind of support a functional style of programming (everything stays in the context of a promise). On the other hand, async/await is designed to make the code look like ordinary imperative code, only asynchronous.
I could be wrong, but I would say whichever one is easier for a team to work with is the one they should use.
To me it seems easier to do error handling when using async/await vs then/catch. Most of the times you need an extra try/catch when you're doing then/catch, but when using async/await both sync and async exceptions are caught.
I've been a professional C, Perl, PHP and Python developer.
I'm an ex-sysadmin from the late 20th century.
These days I do more Javascript and CSS and whatnot, and promote UX and accessibility.
It's only usually used for things added to the language later in my mind. for could be "desugarred" into while for example, but we don't call for sugar in the normal run of things.
You are incorrect about async/await in javascript (and actually in every language that I've looked into the implementation). It is internally just unrolling promises yielded from a generator function.
I was doing this with my own code and libraries like co for years before async/await became popular. I think even q had a version of the concept. The experience was almost identical to async/await - you just had to use do co(function * () { ... }) instead of async function() { } and yield instead of await. The fact that it was such a simple swap-in was one of the major reasons that I and many others opposed the introduction of those features.
async/await is not equivalent to using promises. It provides fundamentally different functionality by wrapping promises with a generator. It is important to recognize the difference, because it affects error handling in a significant way.
In this example, if somethingElse() or anotherThing() throw an exception, we would want doSomething() included in the stack trace, since that's where somethingElse() and anotherThing() are called from. In order to do that, the Javascript engine has to capture and store a trace of the stack where doSomething() is called before moving into somethingElse() since it will be inaccessible after somethingElse() is on the stack. That operation consumes both memory and time.
Using await, there is no need to copy and store the current stack context before the execution of somethingElse(). Simply storing a pointer from somethingElse() to doSomething() is sufficient: during the execution of somethingElse(), doSomething() is still on the stack with its execution suspended, so it's available from inside somethingElse(). If somethingElse() throws an exception, the stack-trace can be reconstructed by traversing the pointer. If anotherThing() throws an exception, the stack-trace can be reconstructed as normal, because we are still within the context of doSomething(). Either way, capturing and storing a stack trace in memory before doSomething completes is no longer necessary, and constructing a stack-trace only happens when its necessary, i.e. an actual exception was thrown from somethingElse() or anotherThing().
For further actions, you may consider blocking this person and/or reporting abuse
We're a place where coders share, stay up-to-date and grow their careers.
It's called syntactic sugar because it doesn't introduce any substantial new feature to the language, it's just a sweeter syntax.
So yes: destructuring and
+=
are examples of syntactic sugar because you could write it in the longer, less clean form, and it would be equivalent.As a side note, sometimes the verb "to desugar" is used to mean "to turn into the equivalent syntax without sugar" As in this example sentence:
Async/await is not an true example of syntactic sugar, it's an entire new feature in the language. You can re-write async/code to use
then
/catch
handlers but it's much more than a simple syntax transformation, it's a re-write.In conclusion, it's called "sugar" probably because it implies it's something you sprinkle onto the language syntax to make it easier, but not a whole new set of features. Under the hood, syntactic sugar could be implemented as a transformation of the syntax tree, so the runtime engine may not even need to know about it.
And I agree with the "diabetes" argument: too much syntactic sugar isn't good, it can definitely be taken too far when it makes code harder to read instead of easier to read.
Thanks for your response.
In terms of
async
/await
, andthen
/catch
. When do you feel is a good time to use either?For asynchronous operations, I have most often resort to
fetch
andthen/catch
. Not a very varied vocabulary.@jenninat0r , It might just be a typo, but I think you meant async/await syntax vs. promises which use then/catch syntax to chain operations (with
.catch(cb)
being used for error handling). Not try/catch.In the case of async/await, you'd just use normal try/catch syntax around your
await
calls for error handling.Both are used to accomplish the same thing, so I'd say which one you use is a matter of preference. Promises are very similar to monads (although technically they don't quite qualify as such), and I suspect that promises were developed to kind of support a functional style of programming (everything stays in the context of a promise). On the other hand, async/await is designed to make the code look like ordinary imperative code, only asynchronous.
I could be wrong, but I would say whichever one is easier for a team to work with is the one they should use.
Yes it was a brain fart. Iโm in the โthen/catchโ camp !
To me it seems easier to do error handling when using async/await vs then/catch. Most of the times you need an extra try/catch when you're doing then/catch, but when using async/await both sync and async exceptions are caught.
It's only usually used for things added to the language later in my mind.
for
could be "desugarred" intowhile
for example, but we don't callfor
sugar in the normal run of things.It's all a bit murky.
You are incorrect about
async/await
in javascript (and actually in every language that I've looked into the implementation). It is internally just unrolling promises yielded from a generator function.I was doing this with my own code and libraries like
co
for years beforeasync/await
became popular. I think evenq
had a version of the concept. The experience was almost identical toasync/await
- you just had to use doco(function * () { ... })
instead ofasync function() { }
andyield
instead ofawait
. The fact that it was such a simple swap-in was one of the major reasons that I and many others opposed the introduction of those features.async
/await
is not equivalent to using promises. It provides fundamentally different functionality by wrapping promises with a generator. It is important to recognize the difference, because it affects error handling in a significant way.Consider:
In this example, if
somethingElse()
oranotherThing()
throw an exception, we would wantdoSomething()
included in the stack trace, since that's wheresomethingElse()
andanotherThing()
are called from. In order to do that, the Javascript engine has to capture and store a trace of the stack wheredoSomething()
is called before moving intosomethingElse()
since it will be inaccessible aftersomethingElse()
is on the stack. That operation consumes both memory and time.Now consider:
Using
await
, there is no need to copy and store the current stack context before the execution ofsomethingElse()
. Simply storing a pointer fromsomethingElse()
todoSomething()
is sufficient: during the execution ofsomethingElse()
,doSomething()
is still on the stack with its execution suspended, so it's available from insidesomethingElse()
. IfsomethingElse()
throws an exception, the stack-trace can be reconstructed by traversing the pointer. IfanotherThing()
throws an exception, the stack-trace can be reconstructed as normal, because we are still within the context ofdoSomething()
. Either way, capturing and storing a stack trace in memory beforedoSomething
completes is no longer necessary, and constructing a stack-trace only happens when its necessary, i.e. an actual exception was thrown fromsomethingElse()
oranotherThing()
.