Created
October 26, 2015 04:17
-
-
Save uniphil/83ccc03e3161f53a7508 to your computer and use it in GitHub Desktop.
thenAlways: safer resource management with promises
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import assert from 'assert'; | |
import { MongoClient as mongoClient } from 'mongodb'; | |
import thenAlways from './then-always'; | |
/** | |
* Use thenAlways to make a helper to safely initialize, use, and clean up a mongodb connection | |
* @param {string} dbURI The mongodb test database to connect to | |
* @param {func} doStuff A callback to run some tasks with the test database | |
* @returns {Promise} A promise resolving or rejecting when everything's done | |
*/ | |
const testWithDB = (dbURI, doStuff) => () => // wrap in an extra fn so we can declare tests without running them | |
thenAlways(mongoClient.connect(dbURI), dbConn => dbConn.close(), dbConn => { | |
const dropTestDB = () => dbConn.command({dropDatabase: 1}); | |
return thenAlways(dropTestDB(), dropTestDB, () => doStuff(dbConn)); // clean out the test db before and after | |
}); | |
// Set up some tests! | |
const TEST_DB_URI = 'mongodb://localhost:27017/demo-testing-temp-database'; | |
const testInsertsWithoutErr = testWithDB(TEST_DB_URI, db => | |
db.collection('testCollection').insert({a: 1})); | |
const testCanFindDoc = testWithDB(TEST_DB_URI, db => { | |
const collection = db.collection('testCollection'); | |
return collection.insert({a: 1}) | |
.then(() => collection.find({}).toArray()) | |
.then(objs => { | |
assert.equal(objs.length, 1, 'Expected to find one document in the collection'); | |
assert.equal(objs[0].a, 1, 'Expected document.a to be 1'); | |
assert.ok(objs[0]._id, 'Expected document to have an _id'); | |
}); | |
}); | |
const testInsertAndFindMany = testWithDB(TEST_DB_URI, db => { | |
const collection = db.collection('testCollection'); | |
return collection.insertMany([{a: 1}, {a: 1, b: 2}]) | |
.then(() => Promise.all([ | |
collection.find({a: 1}).toArray(), | |
collection.find({b: 2}).toArray() | |
])) | |
.then(results => { | |
assert.equal(results[0].length, 2, 'Expected to find 2 {a: 1}'); | |
assert.equal(results[1].length, 1, 'Expected to find 1 {b: 2}'); | |
}); | |
}); | |
// Run the tests! | |
Promise.resolve(console.log('Starting tests...')) // .resolve only so we can start test1 with a .then | |
.then(testInsertsWithoutErr) | |
.then(testCanFindDoc) | |
.then(testInsertAndFindMany) | |
.catch(err => { | |
console.error(`Tests failed: ${err}`) | |
return Promise.reject(err); // re-reject after logging the error | |
}) | |
.then(() => console.log('All tests finished successfully!')); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Safely initialize, use, and clean up a resource | |
* | |
* @param {Promise} initPromise A promise for any kind of resource initialization | |
* @param {func} cleanupFn A callback to clean up the resource, returning a promise if async | |
* @param {func} doStuff Some things to do with the resource, returning a promise if async | |
* @returns {Promise} A promise that resolves or rejects after the resource has been initialized, | |
* the doStuff is finished, and cleanupFn is over. | |
*/ | |
const thenAlways = (initPromise, cleanupFn, doStuff) => | |
initPromise.then(initValue => | |
Promise.resolve(doStuff(initValue)) // cast doStuff's return to a promise | |
.catch(stuffErr => { // doStuff failed | |
cleanupFn(initValue); // clean up regardless | |
return Promise.reject(stuffErr); // re-reject doStuff's error | |
}) | |
.then(() => cleanupFn(initValue))); // clean up if doStuff succeeded | |
// no .catch: if resource init fails, don't doStuff or cleanUp, and let the caller handle it. | |
export default thenAlways; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment