Skip to content

Commit e0755c2

Browse files
committed
feat!: remove the callback-style submit() API
Bottleneck is Promise-only: schedule()/wrap() remain. Removes the submit method
1 parent 5aa0ac5 commit e0755c2

13 files changed

Lines changed: 313 additions & 364 deletions

.oxlintrc.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,7 @@
6969
"expectTypeOf",
7070
"assert",
7171
"assertType",
72-
"assert.**",
73-
"**.noErrVal"
72+
"assert.**"
7473
]
7574
}
7675
]

README.md

Lines changed: 21 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ More importantly, this library has been rewritten with modern-day JS (courtesy o
1818

1919
### Breaking changes in v4
2020

21+
- The callback-style `submit()` method has been removed — Bottleneck is now Promise-only. Use `schedule()`, wrapping callback-style functions with [`util.promisify`](https://nodejs.org/api/util.html#utilpromisifyoriginal). The `Bottleneck.Callback` type is gone from the typings.
2122
- The ES5 build has been removed (`require("bottleneck/es5")` no longer exists). If you need broad-browser support, use the UMD `@sderrow/bottleneck/light` build instead.
2223
- Cluster mode now requires `redis` v4+ (drops v2/v3) or `ioredis` v5+. The unsupported `redis` v2/v3 client API has been removed.
2324
- `ioredis` and `redis` are now optional **peer dependencies**. Your application must install whichever client it uses.
@@ -35,7 +36,6 @@ See [Upgrading to v4](#upgrading-to-v4) for migration steps.
3536
- [Gotchas & Common Mistakes](#gotchas--common-mistakes)
3637
- [Constructor](#constructor)
3738
- [Reservoir Intervals](#reservoir-intervals)
38-
- [`submit()`](#submit)
3939
- [`schedule()`](#schedule)
4040
- [`wrap()`](#wrap)
4141
- [Job Options](#job-options)
@@ -153,16 +153,11 @@ const result = await wrapped(arg1, arg2);
153153

154154
#### ➤ Using callbacks?
155155

156-
Instead of this:
156+
Bottleneck is Promise-only. Wrap callback-style functions with [`util.promisify`](https://nodejs.org/api/util.html#utilpromisifyoriginal) and `schedule()` them:
157157

158158
```js
159-
someAsyncCall(arg1, arg2, callback);
160-
```
161-
162-
Do this:
163-
164-
```js
165-
limiter.submit(someAsyncCall, arg1, arg2, callback);
159+
const promisified = util.promisify(someAsyncCall);
160+
const result = await limiter.schedule(promisified, arg1, arg2);
166161
```
167162

168163
### Step 3 of 3
@@ -219,9 +214,7 @@ limiter.schedule(() => object.doSomething());
219214

220215
- If you plan on using `priorities`, make sure to set a `maxConcurrent` value.
221216

222-
- **When using `submit()`**, if a callback isn't necessary, you must pass `null` or an empty function instead. It will not work otherwise.
223-
224-
- **When using `submit()`**, make sure all the jobs will eventually complete by calling their callback, or set an [`expiration`](#job-options). Even if you submitted your job with a `null` callback , it still needs to call its callback. This is particularly important if you are using a `maxConcurrent` value that isn't `null` (unlimited), otherwise those not completed jobs will be clogging up the limiter and no new jobs will be allowed to run. It's safe to call the callback more than once, subsequent calls are ignored.
217+
- **Make sure your jobs eventually settle** (resolve or reject), or set an [`expiration`](#job-options). With a `maxConcurrent` value that isn't `null` (unlimited), jobs whose promises never settle clog the limiter and no new jobs will be allowed to run.
225218

226219
- Using tools like `mockdate` in your tests to change time in JavaScript will likely result in undefined behavior from Bottleneck.
227220

@@ -301,21 +294,9 @@ Reservoir Intervals are an advanced feature, please take the time to read and un
301294

302295
- **Reservoir Intervals prevent a limiter from being garbage collected.** Call `limiter.disconnect()` to clear the interval and allow the memory to be freed. However, it's not necessary to call `.disconnect()` to allow the Node.js process to exit.
303296

304-
### submit()
305-
306-
Adds a job to the queue. This is the callback version of `schedule()`.
307-
308-
```js
309-
limiter.submit(someAsyncCall, arg1, arg2, callback);
310-
```
311-
312-
You can pass `null` instead of an empty function if there is no callback, but `someAsyncCall` still needs to call **its** callback to let the limiter know it has completed its work.
313-
314-
`submit()` can also accept [advanced options](#job-options).
315-
316297
### schedule()
317298

318-
Adds a job to the queue. This is the Promise and async/await version of `submit()`.
299+
Adds a job to the queue.
319300

320301
```js
321302
const fn = function (arg1, arg2) {
@@ -360,12 +341,9 @@ wrapped()
360341

361342
### Job Options
362343

363-
`submit()`, `schedule()`, and `wrap()` all accept advanced options.
344+
`schedule()` and `wrap()` accept advanced options.
364345

365346
```js
366-
// Submit
367-
limiter.submit({/* options */}, someAsyncCall, arg1, arg2, callback);
368-
369347
// Schedule
370348
limiter.schedule({/* options */}, fn, arg1, arg2);
371349

@@ -1123,6 +1101,20 @@ The same applies to `Bottleneck.Group` and to the standalone `Bottleneck.RedisCo
11231101

11241102
If you previously hit "Bottleneck failed to require ioredis at runtime", that workaround paragraph is no longer needed. The implicit-require hack has been removed entirely.
11251103

1104+
### `submit()` users
1105+
1106+
The callback API is gone. The one-line translation:
1107+
1108+
```js
1109+
// Before
1110+
limiter.submit(someAsyncCall, arg1, arg2, callback);
1111+
1112+
// After
1113+
limiter.schedule(util.promisify(someAsyncCall), arg1, arg2).then(result => /* ... */);
1114+
```
1115+
1116+
Job options move over unchanged: `limiter.schedule({ priority: 4 }, fn, ...args)`.
1117+
11261118
### Legacy `redis` v2/v3 users
11271119

11281120
The minimum supported `redis` package version is now v4. The v2/v3 callback-style client API is no longer supported. Follow node-redis's own [v3-to-v4 migration guide](https://github.com/redis/node-redis/blob/master/docs/v3-to-v4.md) to upgrade your client. v5 is also fully supported.

bottleneck.d.ts

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,6 @@ declare module "bottleneck" {
129129
*/
130130
readonly enqueueErrorMessage?: string | null;
131131
};
132-
type Callback<T> = (err: any, result: T) => void;
133132
type ClientsList = { client?: any; subscriber?: any };
134133
type GroupLimiterPair = { key: string; limiter: Bottleneck };
135134
type Strategy = number & { readonly __brand: "BottleneckStrategy" };
@@ -597,16 +596,6 @@ declare module "bottleneck" {
597596
withOptions: (options: Bottleneck.JobOptions, ...args: Args) => Promise<R>;
598597
};
599598

600-
submit<R, Args extends any[]>(
601-
fn: (...args: [...Args, Bottleneck.Callback<R>]) => void,
602-
...args: [...Args, Bottleneck.Callback<R>]
603-
): void;
604-
submit<R, Args extends any[]>(
605-
options: Bottleneck.JobOptions,
606-
fn: (...args: [...Args, Bottleneck.Callback<R>]) => void,
607-
...args: [...Args, Bottleneck.Callback<R>]
608-
): void;
609-
610599
schedule<R, Args extends any[]>(
611600
fn: (...args: Args) => PromiseLike<R>,
612601
...args: Args

src/Bottleneck.js

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -396,49 +396,6 @@ class Bottleneck {
396396
}
397397
}
398398

399-
submit(...sargs) {
400-
let cb, fn, options;
401-
if (typeof sargs[0] === "function") {
402-
cb = sargs.pop();
403-
[fn, ...sargs] = sargs;
404-
options = parser.load({}, this.jobDefaults);
405-
} else {
406-
cb = sargs.pop();
407-
[options, fn, ...sargs] = sargs;
408-
options = parser.load(options, this.jobDefaults);
409-
}
410-
411-
const task = (...targs) => {
412-
return new Promise((resolve, reject) =>
413-
fn(...targs, (...args) => (args[0] != null ? reject : resolve)(args)),
414-
);
415-
};
416-
417-
const job = new Job(
418-
task,
419-
sargs,
420-
options,
421-
this.jobDefaults,
422-
this.rejectOnDrop,
423-
this.Events,
424-
this._states,
425-
);
426-
// Promise-to-callback bridge for the dual submit()/schedule() API: submit()
427-
// is synchronous and pipes the job's eventual outcome into the Node-style
428-
// callback. The chain form IS the bridge — an async wrapper would just add
429-
// a floating promise around the same pipe.
430-
job.promise
431-
.then((args) => (typeof cb === "function" ? cb(...(args || [])) : undefined))
432-
.catch((args) => {
433-
if (Array.isArray(args)) {
434-
return typeof cb === "function" ? cb(...args) : undefined;
435-
} else {
436-
return typeof cb === "function" ? cb(args) : undefined;
437-
}
438-
});
439-
return this._receive(job);
440-
}
441-
442399
schedule(...args) {
443400
let options, task;
444401
if (typeof args[0] === "function") {

test.ts

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,6 @@ package name via the triple-slash reference above). Checked by
1111
`pnpm tsc` as part of the project tsconfig.
1212
*/
1313

14-
function withCb(foo: number, bar: () => void, cb: (err: any, result: string) => void) {
15-
let s: string = `cb ${foo}`;
16-
cb(null, s);
17-
}
18-
1914
console.log(Bottleneck);
2015

2116
let limiter = new Bottleneck({
@@ -61,17 +56,6 @@ limiter.done().then(function (x) {
6156
let i: number = x;
6257
});
6358

64-
limiter.submit(
65-
withCb,
66-
1,
67-
() => {},
68-
(err, result) => {
69-
let s: string = result;
70-
console.log(s);
71-
assert(s == "cb 1");
72-
},
73-
);
74-
7559
function withPromise(foo: number, bar: () => void): PromiseLike<string> {
7660
let s: string = `promise ${foo}`;
7761
return Promise.resolve(s);
@@ -185,29 +169,6 @@ group.on("created", (limiter, key) => {
185169
assert(key.length > 0);
186170
});
187171

188-
group.key("foo").submit(
189-
withCb,
190-
2,
191-
() => {},
192-
(err, result) => {
193-
let s: string = `${result} foo`;
194-
console.log(s);
195-
assert(s == "cb 2 foo");
196-
},
197-
);
198-
199-
group.key("bar").submit(
200-
{ priority: 4 },
201-
withCb,
202-
3,
203-
() => {},
204-
(err, result) => {
205-
let s: string = `${result} bar`;
206-
console.log(s);
207-
assert(s == "cb 3 foo");
208-
},
209-
);
210-
211172
let f1: Promise<string> = group.key("pizza").schedule(withPromise, 2, () => {});
212173
f1.then(function (result: string) {
213174
let s: string = result;

0 commit comments

Comments
 (0)