#Emberjs2018—further adventures into the future

Chris Masters
15 min readJun 10, 2018

--

First, a quick update

In the first part of this series I explained that it was necessary to install the angle bracket polyfill to start using that syntax today—this is already no longer the case.

Also, I didn’t initially manage to get the CompatComponent from Ember Glimmer Component working to start using glimmer components. Once again, this is no longer an issue either.

I’ve updated the original post to reflect this, but in case you’ve already read it this might be worth mentioning here.

On the other hand, there is currently a bug with the latest version of Ember CLI, so you might want to check this link if you have an issue running ember serve in your own applications using Ember CLI canary too.

Code

This is the commit of the emberjs-2018 repo which reflects the current state of the application, with no polyfill and using ember-glimmer-components.

Introduction

So far my focus has been on file layout and components but with this post I want to switch gears and build on this with some of the forthcoming features that focus more on performance and handling asynchrony.

I’m going to continue developing the same application but progressing with more real-world use cases to illustrate the new functionality.

Promises and the run loop AKA taming asynchronous code AKA async/await

Background

One of the things I’ve found most challenging about learning Ember—or javascript in general for that matter—is to understand how asynchrony is managed.

Javascript is single threaded and therefore uses an event loop to allow things to run concurrently (more than one thing at a time). If you don’t know what I’m talking about then this video is a very good explanation.

Not only that, but Ember itself also has it’s own way of grouping together tasks to make things work more efficiently. This is called the run loop and although the Ember guides provide a very good explanation of the motivation and way to use it I can honestly say it is still one of the parts of Ember that I am least confident about.

Whilst I was learning Ember itself I remember having to also get to grips with understanding promises too. Whilst promises are separate from the framework itself as they’re an integral part of working with Ember the net effect was to increase the learning curve with several different new concepts at the same time.

Maybe things are different now. Perhaps anyone using javascript is already familiar with how promises work as they are more universally adopted, but I still think that as async/await is a more naturally intuitive concept anything like that which can help onboard people to using Ember more easily is a good thing.

Progress

If you have used Ember for a while there is a high chance you have run your test suite and seen the dreaded You have turned on testing mode, which disabled the run-loop's autorun. You will need to wrap any code with asynchronous side-effects in a run loop . The great news is those days may soon be over.

As explained by Edward Faulkner in his excellent answer to a question about promises and RSVP (which is well worth a read in full).

Now that we can leverage the native microtask queue, we can make autoruns as robust as explicit run loops. The question of when to start and end the loop goes away — the native microtask queue is just always present. It’s always safe to schedule more work onto it, and rely on a native-task-based flush that will take care of all the “after” work. So the plan is to remove the assertion and just embrace autoruns, because they become no longer a problem.

Now this sounded brilliant, but it also demanded an understanding why microtasks exist and why they are something that we can now leverage. Again, Edward Faulkner helped me understand.

Up until Ember 3.0 (February 14, 2018) we supported browsers that didn’t have reliable, correct microtask scheduling. So Ember has long had it’s own built-in implementation of this same pattern: the runloop. The runloop achieves the same thing you would with the native microtask queue: it groups together work that’s supposed to go together, and lets you schedule what should happen “after” all that work has finished.

Put simply—before browsers had a native way to package up bits of work into specific tasks there was still a need to do so, hence Ember’s run loop.

Due to this fact promises (and async/await) have also needed to interoperate with the run loop—which is why RSVP is still preferred to native promise support and async/await has not really been adopted outside of writing tests.

This made sense to me—Ember innovated first so needed it’s own library but now that browsers have caught up it can migrate to the native versions, removing some of the now unnecessary prior complexity.

Using now

Unfortunately, I’ve had to regress to using callbacks many times because the Ember Run Loop breaks when testing code written with async/await. He mentioned that this was something that needed to be fixed, and I would suggest it become a priority.

A Road to Ember 4.0 by Andrew Callahan

There are a lot of places where you can already take advantage of them in Ember, particularly in our modern-style tests. But making them work everywhere is the same as making native Promises work everywhere, because they are just syntactic sugar for native promises. So everything above about Ember soon working with native promises also applies to async/await.

— Edward Faulkner post on Ember discuss

Now that all browsers support microtasks it’s just a matter of time before the run loop becomes something which the majority of Ember developers will no longer need to concern themselves with—it will just work.

But how long will this take?

In fact—good news! This issue has already been resolved and Ember is including a version of backburner that uses microtasks since 3.2.0-beta1 !

Knowing that microtasks (and therefore async/await and native promises) are now supported I thought a good place to try this out would be on a model hook.

As Ember ‘blocks’ a route’s template from displaying while waiting for the model’s promise to resolve it makes it a perfect place to load any data that is essential for the route’s content to display.

I began with the current conventional way of doing this—using a promise to delay the model return by two seconds.

A model hook using run.later and RSVP Promise to delay return by 2 seconds

This works as expected, but the same code can now be written without using promises or the run loop and by adopting the new async/await syntax like this

The same hook but using async await and without the run loop

Success

Just as before, viewing this in the browser demonstrates that after a 2 second delay the tomster appears.

Verdict

Personally, I find this style of code, which appears more like synchronous code, easier to rationalise in most instances.

Certainly promises still have their place and are an essential primitive to help manage asynchrony but, for me, there is something very appealing about being able to write asynchronous code in a way that feels much more like synchronous code.

It feels like another step change in making asynchronous code more comprehensible like the evolution from ‘callback hell’ to promises was.

In fact, anyone who has used Alex Matchneer’s Ember Concurrency library (and if you haven’t you should definitely check it out) might think it’s familiar.

Instead of tasks and yield there’s async preceding the method name and await to indicate some asynchronous code.

But Ember Concurrency is much more feature rich with task groups and different built in ways of managing concurrency.

In terms of adopting async/await my main remaining concern was whether or not testing would prove as successful as that seemed to be where in my experience most problems arose handling asynchrony were until now.

Code

Here are commits for the initial current approach and the async/await version to the Emberjs-2018 repo.

Testing

Background

I think it’s fair to say that the testing story in Ember has become one of it’s real strengths.

Up until now I’ve avoided writing about tests in this experiment because, frankly, testing is now so good that it has already adopted into the mainstream many of these features — including the use of async/await.

So before cracking open the champagne on using this new coding style for application code I thought it would be the right moment to add a simple acceptance test which would test the new approach with an async model hook.

As mentioned earlier often the moment trouble rears it’s head using async/await it’s during tests — as this issue for the model hook illustrates.

Using now

Running the test suite on the project showed that there was one failing component test—which was to be expected as the default blueprint test for the tomster-logo component was no longer applicable.

Just in case you are unaware Ember comes pre-wired for testing and running the test suite is as simple as

EMBER_CLI_MODULE_UNIFICATION=true ember test --server

I decided that it made sense to update this test first to get it passing—and to see whether using the angle bracket syntax would cause any problems.

Passing component test using new test syntax and angle bracket components

The next test, which was more relevant to this experiment, was to see if a test would still correctly display the route, using async/await and a setTimeout outside a run loop in the model.

For this it seemed most appropriate to use an acceptance test

EMBER_CLI_MODULE_UNIFICATION=true ember generate acceptance-test home

Then the test visits the application root and checks to see whether the correct text gets displayed.

Testing the async model hook in an acceptance test

Success

Both tests passed with no problems.

Like so many of the forthcoming features in my first post this really does seem to ‘just work’ already now too.

My only slight disappointment was I was unable to get working the qunit-dom library, which seems like a nice way of cleaning up the code for assertions.

I’m still not sure why it didn’t work, as it looks like it is bundled by default in Ember CLI as of v3.2. It’s something I’m going to look into.

Verdict

Honestly, I wasn’t too sure what to expect with this—it’s something which almost seems too good to be true. So far though, writing the tests seemed straightforward using these new features.

There is a great writeup on modern testing in Ember by Robert Jackson Async/Await Configuration Adventure, and I’d also recommend Testing in 2018 for more information on forthcoming features.

This is really going to be a great improvement and even just trying it out for this example makes me eager to start updating existing projects to this new syntax. I think lots of obscure bugs from promise chains will become much more obvious and fixable.

Code

Here is the component test commit and the acceptance test commit in the Emberjs-2018 repo.

Removing jQuery AKA bye bye $, hello fetch

Background

In a similar way that browsers have adopted better support for native promises and microtasks many of the reasons people reached for jQuery over the years have disappeared.

There is a great site You Might Not Need jQuery which attempts to show a non-jQuery equivalent for each potential use case you might have.

Many of these features, like document.querySelectorAll() , which can replace the need to use $() , have already been available in browsers since IE8.

Removing jQuery and other big, potentially optional dependencies from Ember is definitely something that should be carried on. Fewer dependencies equal possibly easier debugging.

My Ember wishlist for 2018 by Dawid Pośliński

One big concern I’ve always had with dropping jQuery support though has not been so much my own application code but relates to AJAX requests.

Since the early days of Ember there have been different solutions to this like ic-ajax and ember-ajax but, as far as I know, they have all built off the underlying jQuery.ajax() function. Ember Data itself, I understood, even uses it under the hood.

Then fetch came along which browsers began to adopt and things got even more confusing, especially with the announcement of Fastboot and with that the need of a dual approach for Ember apps—one for the browser, one for nodejs—became clear.

In fact, it proved to be information overload for me and so I decided to stick with the heard and to use ember-ajax or ember-data as I had always done and let the maintainers of those libraries guide my path.

I see this as another big advantage of using Ember where the community settles around a convention—I can focus on business needs and my own application code, knowing that very smart people are thinking about these hard problems and that sooner or later a good solution and semantic upgrade path will become apparent.

Despite the widespread support for fetch, ember-ajax still relies on jQuery.ajax at its core and Pretender and ember-cli-mirage mock XMLHttpRequest. I hope that these three libraries (and, through AjaxServiceSupport, ember-data) will move to fetch.

— Ember 2018 Roadmap by James Rosen

But now that I’m using Ember CLI canary every time I run ember serve I get a deprecation message about jQuery so it does make me wonder… maybe now it’s finally time to say goodbye to $ once and for all?

Using now

My experimental application doesn’t yet have any AJAX requests or use Ember Data which doesn’t make it a good candidate for resolving any of the concerns I’ve just outlined.

On the other hand that means currently it should be straightforward to remove jQuery.

So my plan is to begin by removing jQuery and then use fetch instead of jQuery.ajax() and once that’s working try to use Ember Data without jQuery too.

There’s an open pull request on getting Ember Data to use fetch, so it seems like eventually that will be the default approach anyway but it’s still in progress.

Running ember serve at the moment gives a deprecation message

DEPRECATION: You have disabled the `jquery-integration` optional feature. You now have to delete `@ember/jquery` from your package.json

So I followed the instructions and removed the dependency

yarn remove @ember/jquery 

Afterwards running ember serve gives a different deprecation warning

DEPRECATION: Ember will stop including jQuery by default in an upcoming version. If you wish keep using jQuery in your application explicitly add `@ember/jquery` to your package.json

I then discovered there was a forthcoming optional feature for jQuery which could be disabled like the application template wrapper and template only components in my previous post.

ember feature:disable jquery-integration

After that ember serve ran with no warnings—and without jQuery…

Introducing fetch

The library Ember Fetch is

An HTML5 fetch polyfill from github wrapped and bundled for ember-cli users

Installing it is as simple as

ember install ember-fetch

To test it using as few external dependencies as possible I created by hand a JSON file for an endpoint in the /public directory.

Incidentally, thanks to Edward Faulkner for using this technique in his Living Animation presentation which is where I discovered it.

public/api/tomsters/1

If you are wondering what this format is, it’s called JSON:API which is a standardised spec for formatting JSON. It’s a convention Ember has adopted to make working with external data more consistent—but if your own service doesn’t conform to it then there are conventions for using any type of data.

src/ui/routes/application/route.js

Now, instead of pausing for 2 seconds, the routes uses fetch to make an asynchronous request for the JSON data, formats it as JSON (another asynchronous method) and returns the correct data.

Using with Ember Data

Most Ember applications use Ember Data to provide an efficient way of managing data. It includes Models and the Store—which is a single source of truth.

It also includes the convention of an adapter—to quote the documentation

An Adapter determines how data is persisted to a backend data store. Things such as the backend host, URL format and headers used to talk to a REST API can all be configured in an adapter.

As mentioned previously, adapters currently do not use fetch.

In the meantime though there is an explanation of how to user ember-fetch with Ember Data in the usage guide.

First, I checked to see if I could use the generate command to create the files in the right locations using the --dry-run flag.

At the time of writing the blueprints for generating models in the module unification style are not working

Blueprints for Ember Data are not yet available, though in progress, so I added the files by hand, following the layout proposed in the RFC.

If you’re doing the same projects like Ghost using module unification can be a useful place to get some insight into the file locations beyond the RFC.

In this case, it means a model file using the classes and the new decorators.

src/data/models/tomster/model.js

and an adapter file—to set the correct host and namespace but most importantly to include the mixin for AdapterFetch .

src/data/models/tomster/adapter.js

You can see that this file still extends the traditional Ember.Object , rather than classes—and this is unfortunately still necessary.

Mixins are one part of the Ember object model that haven’t yet been adopted with classes and decorators. This is because it is still a bit contentious how best to approach it. For a better understanding why, here is an open PR on the ember-decorators repo, and an explanation why this is the case on the ES Classes RFC.

Finally, the route needs to be updated to use Ember Data’s store.

src/ui/routes/application/route.js

Success

It turned out that both using ember-fetch independently and using the AdapterFetch Mixin in an adapter worked fine.

I noticed one deprecation warning for deprecate-copy-copyable but as that deprecation isn’t yet even in the official guide I’m not too concerned.

Verdict

Dropping jQuery was something that rather than being a major headache proved to be straightforward.

Replacing jQuery.ajax was not an issue, besides the need to use a Mixin to support ember-fetch in the adapter.

My hope is that this will be resolved soon, either once this PR gets merged, or more broadly with how Ember will handle Mixins in the future with classes.

One note of caution—whilst this all works fine in application code after removing jQuery as a dependency I can imagine as lots of addons are currently still using jQuery it may still be a necessary dependency projects that use them (until the maintainers have also migrated away).

Code

Here is the commit for using ember-fetch and this is the commit for using Ember Data.

Conclusion

In this second part I’ve now gone deeper into the future way of writing Ember code. Taking a look at how microtasks will unlock native promise and elements of the run loop which will allow features like async/await to be used stress-free.

I feel like this will dramatically reduce some of the hidden complexity which can surface as developers progress with more complex Ember applications.

That day seems very close when articles like this one I wrote on registering waiters in async testing are no longer useful.

Additionally, it’s become clear to me now that using jQuery is definitely no longer obligatory and that thanks to the great testing story test driven development is much more a realistic proposition rather than idealistic goal.

Many of these things can already be tried out today, because the implementations are nearing completion. If we keep executing, a year from now we will take all these features for granted, and that will be wonderful.

— Ember 2018 Priorities by Edward Faulkner

Next time

Before working through these experiments I’d imagined that one post would be enough to cover what I’d hoped to—but that hasn’t turned out to be the case.

In fact, I still haven’t covered a few of the things which are, for me, some of the most important and exciting forthcoming features of Ember.

Maybe I should have covered each topic more briefly but my aim was to explain each point clearly enough so that I (and hopefully others too) could refer back to them as a basic reference, if needed.

That said I don’t want to leave it at this.

My next post is going to focus on using native npm imports, optimizing for filesize and performance (including service workers), and hopefully even how to use Fastboot (with rehydration).

I’d like to end this series by providing a finished demo app with the best of both worlds—a fast initial loading application but with rich functionality.

In the meantime I’d love to hear any comments or feedback on this—especially if you think I’ve missed something important or there is something which could be improved.

Thanks.

--

--