From 27f2a1766c294fb92aba0c20eac3b5de3ce2131b Mon Sep 17 00:00:00 2001 From: Sven Slootweg Date: Tue, 23 Apr 2019 22:40:15 +0200 Subject: [PATCH] Initial commit --- .gitignore | 2 ++ README.md | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++ example.js | 23 ++++++++++++++++ index.js | 23 ++++++++++++++++ package.json | 11 ++++++++ 5 files changed, 137 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 example.js create mode 100644 index.js create mode 100644 package.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..254e3d4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +yarn.lock +node_modules \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..0f2be4d --- /dev/null +++ b/README.md @@ -0,0 +1,78 @@ +# @joepie91/promise-delay-every + +A function for writing *one per N amount of time* rate-limiting implementations. + +When called, it returns a 'delayer' function. This delayer function can be called repeatedly; every time it is called, it returns a Promise that resolves `delay` milliseconds further into the future. The first time it is called, it resolves immediately. + +So for example, if you specify a `delay` of 2000 milliseconds, the Promise returned from the first call will resolve immediately; the Promise returned from the second call will resolve after 2 seconds, the Promise resolved from the third call will resolve after 4 seconds, and so on. + +All of these times are relative to the first call you make - so in the above example, there are 2 seconds between every resolution: + +``` +0ms Call 1 +0ms Resolve 1 +2ms Call 2 +3ms Call 3 +5ms Call 4 +2000ms Resolve 2 +4000ms Resolve 3 +6000ms Resolve 4 +``` + +If you wait longer than the delay to call the delayer function again, the returned Promise will resolve immediately and that time becomes the new base time for future delays: + +``` +0ms Call 1 +0ms Resolve 1 +2ms Call 2 +2000ms Resolve 2 +... time passes, beyond the 2000ms delay ... +7400ms Call 3 +7400ms Resolve 3 +7402ms Call 4 +9400ms Resolve 4 +``` + +## Example + +See also `example.js` for a runnable version. This example uses Bluebird for `Promise.try` (read [this article](http://cryto.net/~joepie91/blog/2016/05/11/what-is-promise-try-and-why-does-it-matter/) to understand why), but Bluebird is not required to use this module. + +```js +"use strict"; + +const Promise = require("bluebird"); +const promiseDelayEvery = require("@joepie91/promise-delay-every"); + +let delayer = promiseDelayEvery(2000); + +return Promise.try(() => { + return delayer(); +}).then(() => { + // Logs immediately after running + console.log("Hello world 1"); + + return delayer(); +}).then(() => { + // Logs 2 seconds after running + console.log("Hello world 2"); + + return delayer(); +}).then(() => { + // Logs 4 seconds after running + console.log("Hello world 3"); +}); +``` + +## API + +### promiseDelayEvery(delay) + +Returns a new `delayer` function. + +- __delay:__ The delay as a number, in millseconds. + +### delayer() + +Returns a new Promise that will resolve at the next scheduled interval, according to the algorithm described at the top of this documentation. + +The Promise will never reject. \ No newline at end of file diff --git a/example.js b/example.js new file mode 100644 index 0000000..43bc679 --- /dev/null +++ b/example.js @@ -0,0 +1,23 @@ +"use strict"; + +const Promise = require("bluebird"); +const promiseDelayEvery = require("./"); + +let delayer = promiseDelayEvery(2000); + +return Promise.try(() => { + return delayer(); +}).then(() => { + // Logs immediately after running + console.log("Hello world 1"); + + return delayer(); +}).then(() => { + // Logs 2 seconds after running + console.log("Hello world 2"); + + return delayer(); +}).then(() => { + // Logs 4 seconds after running + console.log("Hello world 3"); +}); \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..91f33ef --- /dev/null +++ b/index.js @@ -0,0 +1,23 @@ +"use strict"; + +module.exports = function promiseDelayEvery(delay) { + let lastScheduledEmit = 0; + + return function delayer() { + return new Promise((resolve, _reject) => { + let targetTime = lastScheduledEmit + delay; + let currentTime = Date.now(); + + if (currentTime >= targetTime) { + lastScheduledEmit = currentTime; + resolve(); + } else { + lastScheduledEmit = targetTime; + + setTimeout(() => { + resolve(); + }, targetTime - currentTime); + } + }); + } +}; \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..7081a12 --- /dev/null +++ b/package.json @@ -0,0 +1,11 @@ +{ + "name": "@joepie91/promise-delay-every", + "version": "1.0.0", + "main": "index.js", + "repository": "git@git.cryto.net:joepie91/node-promise-delay-every.git", + "author": "Sven Slootweg ", + "license": "WTFPL OR CC0-1.0", + "devDependencies": { + "bluebird": "^3.5.4" + } +}