Skip to content

Instantly share code, notes, and snippets.

@dinks
Created November 20, 2014 09:31
Show Gist options
  • Select an option

  • Save dinks/a724ad6816ab924b03e3 to your computer and use it in GitHub Desktop.

Select an option

Save dinks/a724ad6816ab924b03e3 to your computer and use it in GitHub Desktop.
Promise Patterns
// https://remysharp.com/2014/11/19/my-five-promise-patterns
// Clean shallow chains
var writeFile = Promise.denodeify(fs.writeFile);
writeFile(filename, content)
.then(addDBUser)
.then(dns)
.then(configureHeroku)
.then(function () {
console.log('All done');
});
// If the method relies on Method Context..
var addUser = Promise
.denodeify(model.user.add)
.bind(model.user);
// Prebaking
writeFile(filename, content)
.then(function () {
return addUserToDb('rem', 'password', 'some-db');
});
// this becomes
var addUser = addUserToDb.bind(null, 'rem',
'password', 'some-db');
writeFile(filename, content)
.then(addUser);
// Cold calling
// When a function works both as a promise and using the callback pattern - it's great, but..
function configureHeroku(slug) {
// prebake heroku app create promise
var create = heroku.post.bind(heroku,
'/apps',
{ name: 'example-' + slug }
);
// prebake domain config
var domain = heroku.post.bind(heroku,
'/apps/example-' + slug + '/domains',
{ hostname: slug + '.example.com' }
);
// ** this is where it goes wrong ** //
return create().then(domain);
};
// The issue is when domain is called, it's actually called with the prebaked arguments of the slug and options
// but also the resolved value from create() - so a third argument is received.
// This third argument is the resolved result of create() which is treated as the callback argument
// and as a function object, so the code will try to invoke it - causing an exception.
function coldcall(fn) {
return function () {
fn();
};
}
function configureHeroku(slug) {
// ...
// ** now it works ** //
return create().then(coldcall(domain));
}
// Throw over explicit reject
return new Promise(function (resolve, reject) {
bcrypt.compare(input, password, function (error, result) {
if (error || !result) {
// reject and early exit
return reject(error);
}
resolve(result);
});
});
// becomes
return new Promise(function (resolve) {
bcrypt.compare(input, password, function (error, result) {
if (error) {
throw error;
}
if (!result) {
throw new Error('Passwords did not match.');
}
resolve(result);
});
});
// reject is supposed to be analogous to throw but async. So reject what you'd throw (which is usually an error)
// post with "in ES7 async functions reject is throw".
// This also reinforces that you want to reject with a real error, not a string.
// Always end with a catch
writeFile(filename, content)
.then(addDBUser)
.then(dns)
.then(configureHeroku)
.then(function () {
console.log('All done');
})
.catch(function (error) {
// do something with error
console.log(error.stack);
handle(error);
});
// Note: .catch() is only in the ES6 spec and doesn't appear in Promises/A+
// so some library implementations are missing .catch() support
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment