|
| 1 | +# Twig Asynchronous Rendering |
| 2 | + |
| 3 | +## Synchronous promises |
| 4 | + |
| 5 | +The asynchronous behaviour of Twig.js relies on promises, in order to support both the synchronous and asynchronous behaviour there is an internal promise implementation that runs fully synchronous. |
| 6 | + |
| 7 | +The internal implementation of promises does not use `setTimeout` to run through the promise chain, but instead synchronously runs through the promise chain. |
| 8 | + |
| 9 | +The different promise implementations can be mixed, synchronous behaviour however is no longer guaranteed as soon as the regular promise implementation is run. |
| 10 | + |
| 11 | +### Examples |
| 12 | + |
| 13 | +**Internal (synchronous) implementation** |
| 14 | + |
| 15 | +[Internal implementation](https://github.com/JorgenEvens/twig.js/tree/master/src/twig.async.js#L40) |
| 16 | + |
| 17 | +```javascript |
| 18 | +console.log('start'); |
| 19 | +Twig.Promise.resolve('1') |
| 20 | +.then(function(v) { |
| 21 | + console.log(v); |
| 22 | + return '2'; |
| 23 | +}) |
| 24 | +.then(function(v) { |
| 25 | + console.log(v); |
| 26 | +}); |
| 27 | +console.log('stop'); |
| 28 | + |
| 29 | +/** |
| 30 | + * Prints to the console: |
| 31 | + * start |
| 32 | + * 1 |
| 33 | + * 2 |
| 34 | + * stop |
| 35 | + */ |
| 36 | +``` |
| 37 | + |
| 38 | +**Regular / native promises** |
| 39 | + |
| 40 | +Implementations such as the native promises or [bluebird](http://bluebirdjs.com/docs/getting-started.html) promises. |
| 41 | + |
| 42 | +```javascript |
| 43 | +console.log('start'); |
| 44 | +Promise.resolve('1') |
| 45 | +.then(function(v) { |
| 46 | + console.log(v); |
| 47 | + return '2'; |
| 48 | +}) |
| 49 | +.then(function(v) { |
| 50 | + console.log(v); |
| 51 | +}); |
| 52 | +console.log('stop'); |
| 53 | + |
| 54 | +/** |
| 55 | + * Prints to the console: |
| 56 | + * start |
| 57 | + * stop |
| 58 | + * 1 |
| 59 | + * 2 |
| 60 | + */ |
| 61 | +``` |
| 62 | + |
| 63 | +**Mixing promises** |
| 64 | + |
| 65 | +```javascript |
| 66 | +console.log('start'); |
| 67 | +Twig.Promise.resolve('1') |
| 68 | +.then(function(v) { |
| 69 | + console.log(v); |
| 70 | + return Promise.resolve('2'); |
| 71 | +}) |
| 72 | +.then(function(v) { |
| 73 | + console.log(v); |
| 74 | +}); |
| 75 | +console.log('stop'); |
| 76 | + |
| 77 | +/** |
| 78 | + * Prints to the console: |
| 79 | + * start |
| 80 | + * 1 |
| 81 | + * stop |
| 82 | + * 2 |
| 83 | + */ |
| 84 | +``` |
| 85 | + |
| 86 | + |
| 87 | +## Async helpers |
| 88 | + |
| 89 | +To preserve the correct order of execution there is an implemenation of `Twig.forEach()` that waits any promises returned from the callback before executing the next iteration of the loop. If no promise is returned the next iteration is invoked immediately. |
| 90 | + |
| 91 | +```javascript |
| 92 | +var arr = new Array(5); |
| 93 | + |
| 94 | +Twig.async.forEach(arr, function(value, index) { |
| 95 | + console.log(index); |
| 96 | + |
| 97 | + if (index % 2 == 0) |
| 98 | + return index; |
| 99 | + |
| 100 | + return Promise.resolve(index); |
| 101 | +}) |
| 102 | +.then(function() { |
| 103 | + console.log('finished'); |
| 104 | +}); |
| 105 | + |
| 106 | +/** |
| 107 | + * Prints to the console: |
| 108 | + * 0 |
| 109 | + * 1 |
| 110 | + * 2 |
| 111 | + * 3 |
| 112 | + * 4 |
| 113 | + * finished |
| 114 | + */ |
| 115 | +``` |
| 116 | + |
| 117 | +## Switching render mode |
| 118 | + |
| 119 | +The rendering mode of Twig.js internally is determined by the `allow_async` argument that can be passed into `Twig.expression.parse`, `Twig.logic.parse`, `Twig.parse` and `Twig.Template.render`. Detecting if at any point code runs asynchronously is explained in [detecting asynchronous behaviour](#detecting-asynchronous-behaviour). |
| 120 | + |
| 121 | +For the end user switching between synchronous and asynchronous is as simple as using a different method on the template instance. |
| 122 | + |
| 123 | +**Render template synchronously** |
| 124 | + |
| 125 | +```javascript |
| 126 | +var output = twig({ |
| 127 | + data: 'a {{value}}' |
| 128 | +}).render({ |
| 129 | + value: 'test' |
| 130 | +}); |
| 131 | + |
| 132 | +/** |
| 133 | + * Prints to the console: |
| 134 | + * a test |
| 135 | + */ |
| 136 | +``` |
| 137 | + |
| 138 | +**Render template asynchronously** |
| 139 | + |
| 140 | +```javascript |
| 141 | +var template = twig({ |
| 142 | + data: 'a {{value}}' |
| 143 | +}).renderAsync({ |
| 144 | + value: 'test' |
| 145 | +}) |
| 146 | +.then(function(output) { |
| 147 | + console.log(output); |
| 148 | +}); |
| 149 | + |
| 150 | +/** |
| 151 | + * Prints to the console: |
| 152 | + * a test |
| 153 | + */ |
| 154 | +``` |
| 155 | + |
| 156 | +## Detecting asynchronous behaviour |
| 157 | + |
| 158 | +The pattern used to detect asynchronous behaviour is the same everywhere it is used and follows a simple pattern. |
| 159 | + |
| 160 | +1. Set a variable `is_async = true` |
| 161 | +2. Run the promise chain that might contain some asynchronous behaviour. |
| 162 | +3. As the last method in the promise chain set `is_async = false` |
| 163 | +4. Underneath the promise chain test whether `is_async` is `true` |
| 164 | + |
| 165 | +This pattern works because the last method in the chain will be executed in the next run of the eventloop (`setTimeout`/`setImmediate`). |
| 166 | + |
| 167 | +### Examples |
| 168 | + |
| 169 | +**Synchronous promises only** |
| 170 | + |
| 171 | +```javascript |
| 172 | +var is_async = true; |
| 173 | + |
| 174 | +Twig.Promise.resolve() |
| 175 | +.then(function() { |
| 176 | + // We run our work in here such to allow for asynchronous work |
| 177 | + // This example is fully synchronous |
| 178 | + return 'hello world'; |
| 179 | +}) |
| 180 | +.then(function() { |
| 181 | + is_async = false; |
| 182 | +}); |
| 183 | + |
| 184 | +if (is_async) |
| 185 | + console.log('method ran asynchronous'); |
| 186 | + |
| 187 | +console.log('method ran synchronous'); |
| 188 | + |
| 189 | +/** |
| 190 | + * Prints to the console: |
| 191 | + * method ran synchronous |
| 192 | + */ |
| 193 | +``` |
| 194 | + |
| 195 | +**Mixed promises** |
| 196 | + |
| 197 | +```javascript |
| 198 | +var is_async = true; |
| 199 | + |
| 200 | +Twig.Promise.resolve() |
| 201 | +.then(function() { |
| 202 | + // We run our work in here such to allow for asynchronous work |
| 203 | + return Promise.resolve('hello world'); |
| 204 | +}) |
| 205 | +.then(function() { |
| 206 | + is_async = false; |
| 207 | +}); |
| 208 | + |
| 209 | +if (is_async) |
| 210 | + console.log('method ran asynchronous'); |
| 211 | + |
| 212 | +console.log('method ran synchronous'); |
| 213 | + |
| 214 | +/** |
| 215 | + * Prints to the console: |
| 216 | + * method ran asynchronous |
| 217 | + */ |
| 218 | +``` |
0 commit comments