I spend past year using Dart language as my primary development language at work. I worked on quite complex Angular.Dart single-page application that utilizes high level meta-data driven web components framework, built a latency test driver and ran load tests against backend API/Storage that helped us to speed up our backend and all using Dart. Dart generally positioned as a language for the web, which I believe is a great fit due to sensible language design that you expect from modern languages with great focus on performance but it can be used in a standalone mode on serverside as well. Couple of other aspects that worth mentioning are high quality libraries that are produced by the core team and tooling such as building and packaging (pub) and improving support in editors.
The most interesting thing that I found in Dart platform is Zones API in dart:async package. dart:async provides support for RX style code using Futures and Streams that were influenced by Erik Meijer’s paper. On top of that, Zones API adds very interesting capabilities to the library:
- All the async callbacks are executed in the same zone they were queued in;
- It provides hooks into execution
According to docs “A Zone represents the asynchronous version of a dynamic extent.”. Bit of theory here since the notion of scope is more familiar to most of us, however extent is not so much. Whereas scoping rules refers to symbol resolution:
- Lexical scope: scope of a symbol is limited to body that text or expression. and
- Dynamic scope: scope of a symbol is indefinite once defined and can be used anywhere (global variable)
Extent refers to runtime aspect of the symbol where:
- Indefinite extent: case where symbol refers to same thing indefinitely once defined and can be eventually garbage collected in the languages that support it.
- Dynamic extent: stack-like behavior, where the entity exists for the fixed period and destroyed at the end of it, typically when the control returns to the code that defined the entity.
So in case of Zone callbacks/closures are executed in the same zone that were queued in, allowing us to do very interesting things:
The article on Zones on Dart website covers several usecases for the API where the majority of it focuses on insulating from nested asynchronous invocations.
The other area that is covered is “Overriding Functionality” focuses on superpowers that the API provides. Global method runZoned accepts ZoneSpecification argument that provides a way for the caller to override or intercept small set of specific methods:
runZoned in fact uses fork underneath!
Ability to override creatingTimer and createPeriodicTimer provides a hook that is useful in testing. Interestingly Future[s] callbacks are executed using Timer with duration of 0 (zero).
ZoneSpecification methods in general accept three arguments:
- self [Zone]
- parent [ZoneDelegate]
- originatingZone [Zone]
Therefore, it is possible to delegate to parent Zone instead of handling in current(self) Zone. This is very handy feature for profiling asynchronous applications where the calls are bubbled up to parent zone. If you wonder about third argument originatingZone, it is necessary for some of the invocations such as fork since new zone must be a child of the originating zone.
Another powerful Zone API usecase is clever use by Angular.Dart where it used to implement a wrapper VmTurnZone around zones that is aware of browser’s event loop and allows execution of tasks that need to be completed before browser re-renders by incrementing counter before nested async callbacks are handled and decrementing once a callback is completed. Pre v2 Angular.JS developers had to call $scope.apply for the code that was not managed by Angular and VmTurnZone helped to hide that complexity by calling apply when the even loop has processed the event.
This is one of the APIs that are rarely used, however it is a great example of reifying concepts in core API that greatly simplify other parts of the code.