Skip to content

Commit af36ebb

Browse files
authored
feat(NODE-4521)!: remove custom promise library support (#3498)
1 parent 9cfa2af commit af36ebb

19 files changed

+29
-366
lines changed

etc/notes/CHANGES_5.0.0.md

+5
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,8 @@ npm install --save @aws-sdk/[email protected]
3737
### Minimum supported Node version
3838

3939
The new minimum supported Node.js version is now 14.20.1.
40+
41+
### Custom Promise library support removed
42+
43+
The MongoClient option `promiseLibrary` along with the `Promise.set` export that allows specifying a custom promise library has been removed.
44+
This allows the driver to adopt async/await syntax which has [performance benefits](https://v8.dev/blog/fast-async) over manual promise construction.

package-lock.json

-13
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@
6464
"@types/whatwg-url": "^11.0.0",
6565
"@typescript-eslint/eslint-plugin": "^5.40.0",
6666
"@typescript-eslint/parser": "^5.40.0",
67-
"bluebird": "^3.7.2",
6867
"chai": "^4.3.6",
6968
"chai-subset": "^1.6.0",
7069
"chalk": "^4.1.2",

src/connection_string.ts

-9
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import {
2525
ServerApiVersion
2626
} from './mongo_client';
2727
import { MongoLogger, MongoLoggerEnvOptions, MongoLoggerMongoClientOptions } from './mongo_logger';
28-
import { PromiseProvider } from './promise_provider';
2928
import { ReadConcern, ReadConcernLevel } from './read_concern';
3029
import { ReadPreference, ReadPreferenceMode } from './read_preference';
3130
import type { TagSet } from './sdam/server_description';
@@ -431,10 +430,6 @@ export function parseOptions(
431430
mongoOptions.dbName = 'test';
432431
}
433432

434-
if (options.promiseLibrary) {
435-
PromiseProvider.set(options.promiseLibrary);
436-
}
437-
438433
validateLoadBalancedOptions(hosts, mongoOptions, isSRV);
439434

440435
if (mongoClient && mongoOptions.autoEncryption) {
@@ -960,10 +955,6 @@ export const OPTIONS = {
960955
);
961956
}
962957
},
963-
promiseLibrary: {
964-
deprecated: true,
965-
type: 'any'
966-
},
967958
promoteBuffers: {
968959
type: 'boolean'
969960
},

src/cursor/abstract_cursor.ts

+21-33
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import { TODO_NODE_3286, TypedEventEmitter } from '../mongo_types';
1717
import { executeOperation, ExecutionResult } from '../operations/execute_operation';
1818
import { GetMoreOperation } from '../operations/get_more';
1919
import { KillCursorsOperation } from '../operations/kill_cursors';
20-
import { PromiseProvider } from '../promise_provider';
2120
import { ReadConcern, ReadConcernLike } from '../read_concern';
2221
import { ReadPreference, ReadPreferenceLike } from '../read_preference';
2322
import type { Server } from '../sdam/server';
@@ -297,47 +296,36 @@ export abstract class AbstractCursor<
297296
return bufferedDocs;
298297
}
299298

300-
[Symbol.asyncIterator](): AsyncIterator<TSchema, void> {
301-
async function* nativeAsyncIterator(this: AbstractCursor<TSchema>) {
302-
if (this.closed) {
303-
return;
304-
}
299+
async *[Symbol.asyncIterator](): AsyncIterator<TSchema, void> {
300+
if (this.closed) {
301+
return;
302+
}
305303

306-
while (true) {
307-
const document = await this.next();
304+
while (true) {
305+
const document = await this.next();
308306

309-
// Intentional strict null check, because users can map cursors to falsey values.
310-
// We allow mapping to all values except for null.
311-
// eslint-disable-next-line no-restricted-syntax
312-
if (document === null) {
313-
if (!this.closed) {
314-
const message =
315-
'Cursor returned a `null` document, but the cursor is not exhausted. Mapping documents to `null` is not supported in the cursor transform.';
307+
// Intentional strict null check, because users can map cursors to falsey values.
308+
// We allow mapping to all values except for null.
309+
// eslint-disable-next-line no-restricted-syntax
310+
if (document === null) {
311+
if (!this.closed) {
312+
const message =
313+
'Cursor returned a `null` document, but the cursor is not exhausted. Mapping documents to `null` is not supported in the cursor transform.';
316314

317-
await cleanupCursorAsync(this, { needsToEmitClosed: true }).catch(() => null);
315+
await cleanupCursorAsync(this, { needsToEmitClosed: true }).catch(() => null);
318316

319-
throw new MongoAPIError(message);
320-
}
321-
break;
322-
}
323-
324-
yield document;
325-
326-
if (this[kId] === Long.ZERO) {
327-
// Cursor exhausted
328-
break;
317+
throw new MongoAPIError(message);
329318
}
319+
break;
330320
}
331-
}
332321

333-
const iterator = nativeAsyncIterator.call(this);
322+
yield document;
334323

335-
if (PromiseProvider.get() == null) {
336-
return iterator;
324+
if (this[kId] === Long.ZERO) {
325+
// Cursor exhausted
326+
break;
327+
}
337328
}
338-
return {
339-
next: () => maybeCallback(() => iterator.next(), null)
340-
};
341329
}
342330

343331
stream(options?: CursorStreamOptions): Readable & AsyncIterable<TSchema> {

src/index.ts

-4
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import { GridFSBucketWriteStream } from './gridfs/upload';
1616
import { Logger } from './logger';
1717
import { MongoClient } from './mongo_client';
1818
import { CancellationToken } from './mongo_types';
19-
import { PromiseProvider } from './promise_provider';
2019
import { ClientSession } from './sessions';
2120

2221
/** @internal */
@@ -100,9 +99,6 @@ export {
10099
UnorderedBulkOperation
101100
};
102101

103-
// Deprecated, remove in next major
104-
export { PromiseProvider as Promise };
105-
106102
// enums
107103
export { BatchType } from './bulk/common';
108104
export { GSSAPICanonicalizationValue } from './cmap/auth/gssapi';

src/mongo_client.ts

-6
Original file line numberDiff line numberDiff line change
@@ -228,11 +228,6 @@ export interface MongoClientOptions extends BSONSerializeOptions, SupportedNodeC
228228
forceServerObjectId?: boolean;
229229
/** A primary key factory function for generation of custom `_id` keys */
230230
pkFactory?: PkFactory;
231-
/**
232-
* A Promise library class the application wishes to use such as Bluebird, must be ES6 compatible
233-
* @deprecated Setting a custom promise library is deprecated the next major version will use the global Promise constructor only.
234-
*/
235-
promiseLibrary?: any;
236231
/** The logging level */
237232
loggerLevel?: LegacyLoggerLevel;
238233
/** Custom logger object */
@@ -743,7 +738,6 @@ export interface MongoOptions
743738
| 'monitorCommands'
744739
| 'noDelay'
745740
| 'pkFactory'
746-
| 'promiseLibrary'
747741
| 'raw'
748742
| 'replicaSet'
749743
| 'retryReads'

src/promise_provider.ts

-56
This file was deleted.

src/sessions.ts

+1-3
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import type { MongoClient, MongoOptions } from './mongo_client';
2525
import { TypedEventEmitter } from './mongo_types';
2626
import { executeOperation } from './operations/execute_operation';
2727
import { RunAdminCommandOperation } from './operations/run_command';
28-
import { PromiseProvider } from './promise_provider';
2928
import { ReadConcernLevel } from './read_concern';
3029
import { ReadPreference } from './read_preference';
3130
import { _advanceClusterTime, ClusterTime, TopologyType } from './sdam/common';
@@ -605,8 +604,7 @@ function attemptTransaction<TSchema>(
605604
try {
606605
promise = fn(session);
607606
} catch (err) {
608-
const PromiseConstructor = PromiseProvider.get() ?? Promise;
609-
promise = PromiseConstructor.reject(err);
607+
promise = Promise.reject(err);
610608
}
611609

612610
if (!isPromiseLike(promise)) {

src/utils.ts

+1-10
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import type { Explain } from './explain';
2323
import type { MongoClient } from './mongo_client';
2424
import type { CommandOperationOptions, OperationParent } from './operations/command';
2525
import type { Hint, OperationOptions } from './operations/operation';
26-
import { PromiseProvider } from './promise_provider';
2726
import { ReadConcern } from './read_concern';
2827
import { ReadPreference } from './read_preference';
2928
import { ServerType } from './sdam/common';
@@ -445,17 +444,9 @@ export function maybeCallback<T>(
445444
promiseFn: () => Promise<T>,
446445
callback?: Callback<T> | null
447446
): Promise<T> | void {
448-
const PromiseConstructor = PromiseProvider.get();
449-
450447
const promise = promiseFn();
451448
if (callback == null) {
452-
if (PromiseConstructor == null) {
453-
return promise;
454-
} else {
455-
return new PromiseConstructor((resolve, reject) => {
456-
promise.then(resolve, reject);
457-
});
458-
}
449+
return promise;
459450
}
460451

461452
promise.then(

test/integration/collection-management/promise_collection_db_management.test.ts

-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
import { assert as test, setupDatabase } from '../shared';
22

3-
class CustomPromise extends Promise<void> {}
4-
// @ts-expect-error: Dynamic addition to detect custom promise
5-
CustomPromise.prototype.isCustomMongo = true;
6-
73
describe('Collection Management and Db Management (promise tests)', function () {
84
before(function () {
95
return setupDatabase(this.configuration);

test/integration/crud/promise_stats.test.js

-3
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,6 @@
33
const { assert: test, setupDatabase } = require('../shared');
44
const f = require('util').format;
55

6-
class CustomPromise extends Promise {}
7-
CustomPromise.prototype.isCustomMongo = true;
8-
96
describe('stats', function () {
107
before(function () {
118
return setupDatabase(this.configuration);

test/integration/mongodb-handshake/promise_handshake.test.js

-3
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,6 @@ const { assert: test, setupDatabase } = require('../shared');
33
const f = require('util').format;
44
const { LEGACY_HELLO_COMMAND } = require('../../../src/constants');
55

6-
class CustomPromise extends Promise {}
7-
CustomPromise.prototype.isCustomMongo = true;
8-
96
describe('Handshake', function () {
107
before(function () {
118
return setupDatabase(this.configuration);

test/integration/node-specific/cursor_async_iterator.test.js

-57
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
'use strict';
22

33
const { expect } = require('chai');
4-
const Sinon = require('sinon');
5-
const { Promise: BluebirdPromise } = require('bluebird');
6-
const { PromiseProvider } = require('../../../src/promise_provider');
74

85
describe('Cursor Async Iterator Tests', function () {
96
context('default promise library', function () {
@@ -83,58 +80,4 @@ describe('Cursor Async Iterator Tests', function () {
8380
expect(count).to.equal(1);
8481
});
8582
});
86-
context('custom promise library', () => {
87-
let client, collection, promiseSpy;
88-
beforeEach(async function () {
89-
promiseSpy = Sinon.spy(BluebirdPromise.prototype, 'then');
90-
client = this.configuration.newClient({}, { promiseLibrary: BluebirdPromise });
91-
92-
const connectPromise = client.connect();
93-
expect(connectPromise).to.be.instanceOf(BluebirdPromise);
94-
await connectPromise;
95-
const docs = Array.from({ length: 1 }).map((_, index) => ({ foo: index, bar: 1 }));
96-
97-
collection = client.db(this.configuration.db).collection('async_cursor_tests');
98-
99-
await collection.deleteMany({});
100-
await collection.insertMany(docs);
101-
await client.close();
102-
});
103-
104-
beforeEach(async function () {
105-
client = this.configuration.newClient();
106-
await client.connect();
107-
collection = client.db(this.configuration.db).collection('async_cursor_tests');
108-
});
109-
110-
afterEach(() => {
111-
promiseSpy.restore();
112-
PromiseProvider.set(null);
113-
return client.close();
114-
});
115-
116-
it('should properly use custom promise', async function () {
117-
const cursor = collection.find();
118-
const countBeforeIteration = promiseSpy.callCount;
119-
for await (const doc of cursor) {
120-
expect(doc).to.exist;
121-
}
122-
expect(countBeforeIteration).to.not.equal(promiseSpy.callCount);
123-
expect(promiseSpy.called).to.equal(true);
124-
});
125-
126-
it('should properly use custom promise manual iteration', async function () {
127-
const cursor = collection.find();
128-
129-
const iterator = cursor[Symbol.asyncIterator]();
130-
let isDone;
131-
do {
132-
const promiseFromIterator = iterator.next();
133-
expect(promiseFromIterator).to.be.instanceOf(BluebirdPromise);
134-
const { done, value } = await promiseFromIterator;
135-
if (done) expect(value).to.be.a('undefined');
136-
isDone = done;
137-
} while (!isDone);
138-
});
139-
});
14083
});

0 commit comments

Comments
 (0)