There are a couple of antipatterns that make the code messier than necessary. One uses .then
inside a .then
callback when you need to bind them. The other is a delayed antipattern .
First, create one function for reading and unloading, both of which process the corresponding error and cause a new error with some context:
function readAndHandle(att) { return readFile(att.path) .catch(function (error) { throw new Error("Error encountered when reading " + att.path + error); }); } function uploadAndHandle(att, content) { return uploadAttachment({file: att.name, content: content}) .catch(function (error) { throw new Error("Error encountered when uploading " + att.path + error); }); }
Then, combine the two into a function that first reads the file and then loads it. This function returns a promise:
// returns a promise for an uploaded file ID function readAndUpload(att) { return readAndHandle(att) .then(function (content) { return uploaAndHandle(att, content); }); }
Now you can use .map()
to map an array of attachments to an array of promises for file identifiers:
var uploadedIdsPromise = kwargs.attachments.map(readAndUploadAsync);
And this is what you can pass to when.all()
. The .then
handler on this passes an array of identifiers to its callback:
return when.all(uploadedIdsPromise) .then(function (ids) { kwargs.attachments = ids.join(";"); return sendEmail(kwargs); }) .catch(function (error) {
and that is pretty much the essence of it.
It is worth noting here that with the exception of one place that modifies the kwargs
variable, promises does not change anything outside the promise chain. This helps maintain consistency in cleanliness and modularity.
Note that in the above code there is no d.resolve
or d.reject
. The only time you ever use deferred
is when you don't have any promises available yet (or in some other special situations). And even then there are preferred ways to create promises these days.
API documents. API APIs. say the following:
Note. Using when.defer is not recommended. In most cases, using when.promise, when.try, or when.lift provides a better separation of problems.
The current recommended way to create promises from some do not promise asynchronous APIs is to use constructor template expansion . To take your uploadAttachment()
function as an example, it would look like this:
function uploadAttachment(obj) { return when.promise(function (resolve, reject) { setTimeout(function() { resolve(new Date().valueOf());
This is how the ES6 API works, and it eliminates the need to shuffle around a deferred
object.