Async Taglib // MarkoImprove this page

Note: This functionality used to be provided by the <async-fragment> tag which has been deprecated in favor of <await>.

Example

Pass a promise (or callback) to the template:

var template = require('./template.marko');
var request = require('request-promise');
 
module.exports = template.stream({
    userDataProvider: request.get('https://api.example.com/users/123')
});

And await the data:

<await(user from data.userDataProvider)>
    <ul>
        <li>Name: ${user.firstName} ${user.lastName}</li>
        <li>Email address: ${user.email}</li>
    </ul>
</await>

Philosophy

Marko includes a taglib that supports the more efficient and simpler "Pull Model "approach to providing templates with view model data.

The Pull Model approach to template rendering requires the use of a templating engine that supports asynchronous template rendering (e.g. marko and dust). This is because before rendering the template begins not all of data may have been fully retrieved. Parts of a template that depend on data that is not yet available are rendered asynchronously with the Pull Model approach.

Push Model versus Pull Model

The problem with the traditional Push Model approach is that template rendering is delayed until all data has been fully received. This reduces the time to first byte, and it also may result in the server sitting idle while waiting for data to be loaded from remote services. In addition, if certain data is no longer needed by a template then only the template needs to be modified and not the controller.

With the new Pull Model approach, template rendering begins immediately. In addition, sections of the template that depend on data from data providers are rendered asynchronously and await only the associated data provider's completion. The template rendering will only be delayed for data that the template actually needs.

Out-of-order Flushing

The marko-async taglib also supports out-of-order flushing. Enabling out-of-order flushing requires two steps:

  1. Add the client-reorder attribute to the <await> tag:

     <await(user from data.userDataProviderclient-reorder=true>
         <ul>
             <li>Name: ${user.firstName} ${user.lastName}</li>
             <li>Email address: ${user.email}</li>
         </ul>
     </await>
  2. Add the <await-reorderer> to the end of the page.

     <html>
     ...
     <body>
         ...
         <await-reorderer/>
     </body>
     </html>

If client-reorder is true then a placeholder element will be rendered to the output instead of the final HTML for the await instance. The instance will be instead rendered at the end of the page and client-side JavaScript code will be used to move the await's contents into the proper place in the DOM. The <await-reorderer> will be where the out-of-order instances are rendered before they are moved into place. If there are any out-of-order instances then inline JavaScript code will be injected into the page at this location to move the DOM nodes into the proper place in the DOM.

Events

You may listen to these events on the AsyncStream returned from a template's render method or the wrapped stream if it is an event emitter (like node's http res stream).

Taglib API

<await>

Required Argument:

<await(varName from data.provider)>

Supported Attributes:

<await-placeholder>

This tag can be used to control what text is shown while an out-of-order await instance is waiting to be loaded. Only applicable if client-reorder is set to true.

Example:

<await(user from data.userDataProviderclient-reorder>
    <await-placeholder>
        Loading user data...
    </await-placeholder>
 
    <ul>
        <li>First name: ${user.firstName}</li>
        <li>Last name: ${user.lastName}</li>
    </ul>
 
</await>

<await-error>

This tag can be used to control what text is shown when a data provider errors out.

Example:

<await(user from data.userDataProvider)>
    <await-error>
        An error occurred!
    </await-error>
 
    <ul>
        <li>First name: ${user.firstName}</li>
        <li>Last name: ${user.lastName}</li>
    </ul>
</await>

<await-timeout>

This tag can be used to control what text is shown when a data provider times out.

Example:

<await(user from data.userDataProvider)>
    <await-timeout>
        A timeout occurred!
    </await-timeout>
 
    <ul>
        <li>First name: ${user.firstName}</li>
        <li>Last name: ${user.lastName}</li>
    </ul>
</await>

<await-reorderer>

Container for all out-of-order await instances. If any <await> tags have client-reorder set to true then this tag needs to be included in the page template (typically, right before the closing </body> tag).

Example:

<!DOCTYPE html>
<html>
...
<body>
    ...
    <await-reorderer/>
</body>
</html>