User talk:Poltawski/Javascript promises: Difference between revisions
Line 44: | Line 44: | ||
==== Correct ==== | ==== Correct ==== | ||
<code javascript> | <code javascript> | ||
str.get_string('title') | |||
.then(function(title) { | |||
return templates.renderPix(image, 'core', title); | |||
}).then(function(pixhtml) { | |||
actionitem.find('.icon').replaceWith(pixhtml); | |||
return; | |||
}); | |||
$.when(Str.get_string('competencypicker', 'tool_lp'), renderPromise()) | $.when(Str.get_string('competencypicker', 'tool_lp'), renderPromise()) | ||
.then(function(title, html) { | .then(function(title, html) { |
Revision as of 14:17, 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
Correct
str.get_strings(stringRequests).then(function(title) {
return templates.renderPix(image, 'core', title);
}).then(function(pixhtml) {
$('#selector').html(pixhtml);
makeUIVisible();
return;
});
Incorrect
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();
});
Chain rather than nest
Promises as are a construct which help prevent the pyramid of doom and regain linear control flow and error handling. They should not be nested.
- Promises should be chained rather than nested.
- $.when() should be used to deal with the result of multiple promises
Correct
str.get_string('title')
.then(function(title) {
return templates.renderPix(image, 'core', title);
}).then(function(pixhtml) {
actionitem.find('.icon').replaceWith(pixhtml);
return;
});
$.when(Str.get_string('competencypicker', 'tool_lp'), renderPromise())
.then(function(title, html) {
self._popup = new Dialogue(title, html);
});
Incorrect
return renderPromise().then(function (html) {
return Str.get_string('competencypicker', 'tool_lp').then(function(title) {
self._popup = new Dialogue(title, html);
});
});
Avoid mixing callbacks and promises
Avoid mixing callbacks and promises. Design code to embrace promise-y patterns for asynchronous code to make maximum use of promises.
Correct
function doWork(input) {
return renderPromise();
}
doWork(input).then(function () {
$('#selector').html(html);
return;
}).catch(Notification.exception);
Incorrect
function doWork(input, successHandler, errorHandler) {
renderPromise().then(function (html) {
successHandler(html)
}).catch(errorHandler);
}
doWork(input, function () {
$('#selector').html(html);
}, function (error){
Notification.exception(error);
});