Skip to content

Instantly share code, notes, and snippets.

@LiamKarlMitchell
Last active October 12, 2018 03:27
Show Gist options
  • Save LiamKarlMitchell/7185433f5f62ce04e6218d76b2fdf3ec to your computer and use it in GitHub Desktop.
Save LiamKarlMitchell/7185433f5f62ce04e6218d76b2fdf3ec to your computer and use it in GitHub Desktop.
Javascript Async Patterns
// Given an Async Function (Not a promise) this class will ensure that the run method can only run 1 instance of that async operation at a time.
class AsyncSingleInstance {
// Soon we will have private fields? https://github.com/tc39/proposal-class-fields#private-fields
//#inProgress;
//#asyncFunction;
constructor(asyncFunction) {
this.inProgress = false;
this.asyncFunction = asyncFunction;
}
get isRunning() {
return this.inProgress;
}
// Ensures only one instance of the asyncFunction will run at once, will return a promise that resolves to the result or errors if already in use.
async run() {
if (this.inProgress) {
throw new Error('Task '+this.asyncFunction.name+' is already in progress.');
}
this.inProgress = true;
try {
var result = await this.asyncFunction(...arguments);
// Ensure we are not in progress anymore.
this.inProgress = false;
return result;
} catch(e) {
// Ensure we are not in progress anymore.
this.inProgress = false;
throw e;
}
}
}
module.exports = AsyncSingleInstance;
// Useful for mocking a long to run async process.
function delay(ms, result) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve(result);
}, ms);
});
}
function delayError(ms, error) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
reject(error);
}, ms);
});
}
module.exports.delay = delay;
module.exports.delayError = delayError;
async function Disposable(delegate, action, argArray = [], disposer = "dispose") {
var disposable = await Promise.resolve(delegate());
try {
var results = await action(disposable, ... argArray);
} finally {
if (typeof disposer === 'string' || disposer instanceof String) {
if (disposable[disposer] instanceof Function) {
await Promise.resolve(disposable[disposer]());
}
} else if (disposer instanceof Function) {
await Promise.resolve(disposer(disposable));
}
}
return results;
}
module.exports = Disposable;
@LiamKarlMitchell
Copy link
Author

Idea for having a friendlier to call syntax for Disposable for a specific purpose such as getting a mysql connection from a pool.
Would need to make a custom one that uses "release" as the disposer

const mysql = require('mysql');
const pool = mysql.createPool({}); // Config as needed.

const UsingConnection = Disposable.bind(pool.getConnection.bind(pool));

module.exports.UsingConnection = UsingConnection;


// Usage

const { UsingConnection } = require('./MyConnection');

UsingConnection(async function(connection) {
 //...	
});

Of course could always have one where there are two args for the object to call on for a delegate and the delegate action to call
UsingConnectionPool(pool, asyncAction)...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment