User talk:Poltawski/Javascript promises: Difference between revisions
Line 3: | Line 3: | ||
Promises are used extensively in modern Moodle Javascript APIs to handle asynchronous situations. It is unfortunately common to misunderstand how they operate and introduce bugs which only expose themselves in asynchronous edge cases (see article [https://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html we have a problem with promises]). | Promises are used extensively in modern Moodle Javascript APIs to handle asynchronous situations. It is unfortunately common to misunderstand how they operate and introduce bugs which only expose themselves in asynchronous edge cases (see article [https://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html we have a problem with promises]). To encourage promise usage to be more understandable, consistent and avoid edge case bugs, we have adopted best practices suggested by Nolan Lawson and verified by [https://github.com/xjamundx/eslint-plugin-promise eslint-plugin-promise]. | ||
=== | === Always return or throw === | ||
When writing promises they must: | |||
* return another promise | * return another promise, or | ||
* return a synchronous value (or undefined) | * return a synchronous value (or undefined), or | ||
* throw a synchronous error | * throw a synchronous error | ||
Good: | |||
<code> | |||
// Good: | |||
str.get_strings(stringRequests).then(function(title) { | |||
return templates.renderPix(image, 'core', title); | |||
}).then(function(pixhtml) { | |||
$('#selector').html(pixhtml); | |||
makeUIVisible(); | |||
return; | |||
}); | |||
// Bad: | |||
str.get_strings(stringRequests).then(function(title) { | |||
templates.renderPix(image, 'core', title).function(pixhtml) { | |||
$('#selector').html(pixhtml); | |||
}); | |||
}).then(function() { | |||
// Wrong because renderPix() has not guaranted to be resolved here. | |||
makeUIVisible(); | |||
}); | |||
</code> | |||
=== Do not nest === | === Do not nest === | ||
The great advantage of promises is keeping async code linear rather than nested in <i>callback hell</i> and handling errors in one place, by nesting promises these advantages are lost. | The great advantage of promises is keeping async code linear rather than nested in <i>callback hell</i> and handling errors in one place, by nesting promises these advantages are lost. | ||
<code> | |||
// GOOD: | |||
renderPromise().then(function (html) { | |||
return Str.get_string('competencypicker', 'tool_lp'); | |||
}).then(function(title) { | |||
self._popup = new Dialogue(title, html); | |||
}); | |||
// BAD: | |||
return renderPromise().then(function (html) { | |||
return Str.get_string('competencypicker', 'tool_lp').then(function(title) { | |||
self._popup = new Dialogue(title, html); | |||
}); | |||
}); | |||
</code> |
Revision as of 13:35, 12 June 2017
Promises
Promises are used extensively in modern Moodle Javascript APIs to handle asynchronous situations. It is unfortunately common to misunderstand how they operate and introduce bugs which only expose themselves in asynchronous edge cases (see article we have a problem with promises). To encourage promise usage to be more understandable, consistent and avoid edge case bugs, we have adopted best practices suggested by Nolan Lawson and verified by eslint-plugin-promise.
Always return or throw
When writing promises they must:
- return another promise, or
- return a synchronous value (or undefined), or
- throw a synchronous error
Good:
// Good:
str.get_strings(stringRequests).then(function(title) {
return templates.renderPix(image, 'core', title);
}).then(function(pixhtml) {
$('#selector').html(pixhtml);
makeUIVisible();
return;
});
// Bad:
str.get_strings(stringRequests).then(function(title) {
templates.renderPix(image, 'core', title).function(pixhtml) {
$('#selector').html(pixhtml);
});
}).then(function() {
// Wrong because renderPix() has not guaranted to be resolved here.
makeUIVisible();
});
Do not nest
The great advantage of promises is keeping async code linear rather than nested in callback hell and handling errors in one place, by nesting promises these advantages are lost.
// GOOD:
renderPromise().then(function (html) {
return Str.get_string('competencypicker', 'tool_lp');
}).then(function(title) {
self._popup = new Dialogue(title, html);
});
// BAD:
return renderPromise().then(function (html) {
return Str.get_string('competencypicker', 'tool_lp').then(function(title) {
self._popup = new Dialogue(title, html);
});
});