Last active
May 13, 2016 17:21
-
-
Save designfrontier/5c8624c63d719f5b3da4a77dd0802db5 to your computer and use it in GitHub Desktop.
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
const ajax = require('utils/ajax'); | |
const Promise = require('Promise'); | |
/* | |
* The queue itself... | |
* | |
* @param {String} retryConut The number of times that it should retry a request | |
* before it gives up and rejects it. | |
* | |
* @return {object} | |
*/ | |
const Queue = function (retryCount = 3) { | |
const saveQueue = { | |
items: {}, | |
order: [] | |
}; | |
let isRunning = false; | |
/* | |
* This little fellow returns the interval for the next attempt | |
* Basically he lets us increment the interval time as the number of | |
* failures increases. | |
* | |
* @param {Number} attempt The number of the current attempt | |
* | |
* @return {Number} The amount of time in ms to wait for the next attempt | |
*/ | |
const getAttemptInterval = (attempt) => { | |
if (attempt <= 1) { | |
return 0; | |
} else { | |
return Math.pow(attempt, 2) * 25; | |
} | |
}; | |
/* | |
* Cleans up the items in teh queue so they don't stick around after they are | |
* done being processed. | |
* @return {undefined} | |
*/ | |
const cleanUpQueue = () => { | |
saveQueue.items = Object.keys(saveQueue.items).reduce((result, key) => { | |
if (saveQueue.order.indexOf(key) !== -1) { | |
result[key] = saveQueue.items[key]; | |
} | |
return result; | |
}, {}); | |
}; | |
/* | |
* This is the main engine of the queue it is a recursive function that | |
* handles chewing through the changes that need to be saved to the server. | |
* | |
* It calls itself through setTimeout to prevent it from locking down the | |
* event loop indefinitely while it processes things. | |
* | |
* | |
* @returns {undefined} | |
*/ | |
const processQueue = () => { | |
const itemId = saveQueue.order.shift(); | |
const item = itemId ? saveQueue.items[itemId] : {}; | |
if (typeof itemId === 'undefined') { | |
isRunning = false; | |
return; | |
} | |
isRunning = true; | |
item.attempts++; | |
ajax({ | |
url: item.url, | |
type: item.verb, | |
data: item.data, | |
dataType: 'json' | |
}).then((result) => { | |
item.resolve(result); | |
setTimeout(processQueue, getAttemptInterval(0)); | |
cleanUpQueue(); | |
}, (err) => { | |
if (item.attempts >= retryCount) { | |
item.reject(err); | |
} else { | |
saveQueue.order.unshift(itemId); | |
} | |
setTimeout(processQueue, getAttemptInterval(item.attempts)); | |
}); | |
}; | |
/* | |
* This little guy starts up the processing of the queue if it is not running | |
* | |
* @return undefined | |
*/ | |
const startQueue = () => { | |
if (!isRunning) { | |
setTimeout(processQueue, getAttemptInterval(0)); | |
} | |
}; | |
/* | |
* This function adds items to the queue for processing later on | |
* | |
* @param {String} url this will also be used as the ID so should be unique | |
* | |
* @param {Object} data The data that needs to be saved in the format that | |
* server expects it in. | |
* | |
* @param {String} verb The verb that the server πis expecting. Defaults to PUT | |
* | |
* @return {Promise} The promise that will be rejected or resolved when the | |
* queue is finished doing its thing. Rejected if errors or too many | |
* retries. Resolved if everything ends up working out in the end. | |
*/ | |
const add = (url, data, verb = 'PUT') => { | |
startQueue(); | |
if (typeof saveQueue.items[url] !== 'undefined') { | |
// this guy is already in the queue | |
// It might make sense to make this behavior more interesting in the | |
// future, but for now it is good enough™ | |
return saveQueue.items[url].promise; | |
} else { | |
saveQueue.items[url] = { | |
attempts: 0, | |
data: data, | |
url: url, | |
verb: verb, | |
}; | |
saveQueue.items[url].promise = new Promise((resolve, reject) => { | |
saveQueue.items[url].resolve = resolve; | |
saveQueue.items[url].reject = reject; | |
}); | |
saveQueue.order.push(url); | |
return saveQueue.items[url].promise; | |
} | |
}; | |
const getQueue = () => { | |
return saveQueue; | |
}; | |
return { | |
add: add, | |
start: startQueue, | |
get: getQueue | |
} | |
}; | |
module.exports = Queue; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment