Article summary
This is the second post in a series of two that cover capturing, storing, and retrying network requests using Ember and Ember Data. In the first post, I discussed how to intercept HTTP requests and store those requests in local storage. In this post, I will cover how to retrieve them from local storage and retry them.
Retrying Requests
In the previous post, we set ourselves up pretty well to be able to retry requests. In fact, we have an in-memory representation of those requests. They are stored in the queue
of SyncQueue
. As long as the browser isn’t closed or refreshed, we can use that array to begin a retry cycle (we’ll cover what happens if the browser is closed later).
Here is what it looks like to retry a request:
syncQueue: Em.inject.service('synchronization-queue')
processRequests: ->
entry = @get('syncQueue.firstObject')
if entry?
# deserialize entry into request
opts = @get('syncQueueEntryRepository').jqueryOptsForEntry(entry)
# Make the request
Ember.$.ajax(opts).then (result) =>
# Success case
Em.run =>
# Request was successful, so remove that entry
@get('syncQueue').removeEntry(entry).then (val) =>
# Push resulting objects into the Store
@handleResponse(result, entry)
# Process the next request
Em.run.once @, 'processRequest'
, if ((not jqXHR.readyState?) or jqXHR.readyState == 0)
# Connectivity problem -- keep retrying
Em.run =>
# Update a retry timer
@_backoffRetrySyncTime()
# Re-run the request after the backoff has ended
Ember.run.later @, 'processRequest', @get('retrySyncInSeconds') * 1000
The code above boils down to this:
- Get the first request from local storage.
- Make that request.
- If successful, try the next request.
- If not successful, wait N seconds before retrying again.
Retrying Requests After Closing the Browser
But what happens if the user closes the browser before all of the items in the queue have a chance to be processed? This is where we really see the benefit of storing the requests in local storage.
We added some logic in our ApplicationRoute
to help us recover lost data from a browser close or refresh. In our afterModel
hook, we check the contents of local storage for that user. If there are any items in local storage, we re-populate the queue with those items and can then begin the synchronization process.
Here is what our afterModel
function looked like:
afterModel: (resolvedModel) ->
# Load from local storage
@get('synchronizer.syncQueue').loadStateFromPersistentStorage(user.get('id')).then (entries) ->
if entries.length > 0
# Re-populate queue
@get('synchronizer.syncQueue').replaceWithEntries(entries)
# start syncing
@get('synchronizer').startSync()
Review
Let’s take a second to review exactly what we have accomplished. When a request is made, it is captured and stored in local storage. We then process those entries from local storage. When a request is successful, we remove that entry and proceed to the next one. If a request fails, we stop synchronizing and retry later. When the user closes the browser without finishing a synchronization, we have the ability to pick up where we left off when they open the app again.
All of the code is available on GitHub.
oh another coffeescript again :(
please use javascript instead, as a random visitor coffeescript is not readable to me :(
Same here. Please use javascript.