Skip to content

Commit a839c84

Browse files
Fix: use native promises, closes #711, closes #686
* refactor: use native promises and drop q migrated away from q, and used native promises. unhandled rejections in unit tests now cause the test runner to fail. ensured all rejections are ignored with no-op fn. found and fixed several broken tests that did not report failures correctly. fixed wait() not used correctly outside .then, as it's returning a higher order fn rather than the promise (to chain values in .then callbacks) implemented a tiny test polyfill for Promise.allSettled to support older Node versions. worked around a timezone issue and a test failing when running it in ~1am locally, heh. added ability to use .only/skip on the test proxy of mocha's describe fn * fix: full q backwards compatibility layer * chore: better naming * fix: flakiness * fix: flakiness --------- Co-authored-by: Avi Vahl <[email protected]>
1 parent 987f547 commit a839c84

31 files changed

+857
-541
lines changed

lib/api_client/execute_request.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
const config = require("../config");
33
const https = /^http:/.test(config().upload_prefix) ? require('http') : require('https');
44
const querystring = require("querystring");
5-
const Q = require('q');
65
const url = require('url');
76
const utils = require("../utils");
87
const ensureOption = require('../utils/ensureOption').defaults(config());
@@ -13,7 +12,7 @@ const agent = config.api_proxy ? new https.Agent(config.api_proxy) : null;
1312

1413
function execute_request(method, params, auth, api_url, callback, options = {}) {
1514
method = method.toUpperCase();
16-
const deferred = Q.defer();
15+
const deferred = utils.deferredPromise();
1716

1817
let query_params, handle_response; // declare to user later
1918
let key = auth.key;

lib/uploader.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
const fs = require('fs');
22
const { extname, basename } = require('path');
3-
const Q = require('q');
43
const Writable = require("stream").Writable;
54
const urlLib = require('url');
65

@@ -466,7 +465,7 @@ function call_api(action, callback, options, get_params) {
466465

467466
const USE_PROMISES = !options.disable_promises;
468467

469-
let deferred = Q.defer();
468+
const deferred = utils.deferredPromise();
470469
if (options == null) {
471470
options = {};
472471
}

lib/utils/index.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ const {
9292
SUPPORTED_SIGNATURE_ALGORITHMS,
9393
DEFAULT_SIGNATURE_ALGORITHM
9494
} = require('./consts');
95+
const applyQCompat = require("./qPolyfill");
9596

9697
function textStyle(layer) {
9798
let keywords = [];
@@ -1652,6 +1653,23 @@ function jsonArrayParam(data, modifier) {
16521653
*/
16531654
exports.NOP = function () {
16541655
};
1656+
1657+
function deferredPromise() {
1658+
let resolve, reject
1659+
const promise = new Promise((_resolve, _reject) => {
1660+
resolve = _resolve;
1661+
reject = _reject;
1662+
});
1663+
applyQCompat(promise);
1664+
return {
1665+
resolve,
1666+
reject,
1667+
promise
1668+
};
1669+
}
1670+
1671+
exports.deferredPromise = deferredPromise;
1672+
16551673
exports.generate_auth_token = generate_auth_token;
16561674
exports.getUserAgent = getUserAgent;
16571675
exports.build_upload_params = build_upload_params;

lib/utils/qPolyfill.js

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
const scheduleCompatCallback = typeof setImmediate === 'function'
2+
? (fn) => setImmediate(fn)
3+
: (fn) => setTimeout(fn, 0);
4+
5+
function qFinally(onFinally) {
6+
const handler = typeof onFinally === 'function' ? onFinally : () => onFinally;
7+
const PromiseCtor = typeof this.constructor === 'function' && typeof this.constructor.resolve === 'function'
8+
? this.constructor
9+
: Promise;
10+
return this.then(
11+
(value) => PromiseCtor.resolve(handler()).then(() => value),
12+
(reason) => PromiseCtor.resolve(handler()).then(() => {
13+
throw reason;
14+
})
15+
);
16+
}
17+
18+
function qFin(handler) {
19+
return this.finally(handler);
20+
}
21+
22+
function qDone(onFulfilled, onRejected) {
23+
this.then(onFulfilled, onRejected).catch((err) => {
24+
scheduleCompatCallback(() => {
25+
throw err;
26+
});
27+
});
28+
}
29+
30+
function qNodeify(callback) {
31+
if (typeof callback !== 'function') {
32+
return this;
33+
}
34+
35+
this.then(
36+
(value) => scheduleCompatCallback(() => callback(null, value)),
37+
(error) => scheduleCompatCallback(() => callback(error))
38+
);
39+
40+
return this;
41+
}
42+
43+
function qFail(onRejected) {
44+
return this.catch(onRejected);
45+
}
46+
47+
function qProgress() {
48+
return this;
49+
}
50+
51+
function qSpread(onFulfilled, onRejected) {
52+
return this.then(
53+
(values) => {
54+
if (typeof onFulfilled !== 'function') {
55+
return values;
56+
}
57+
if (Array.isArray(values)) {
58+
// eslint-disable-next-line prefer-spread
59+
return onFulfilled.apply(void 0, values);
60+
}
61+
return onFulfilled(values);
62+
},
63+
onRejected
64+
);
65+
}
66+
67+
function applyQCompat(promise) {
68+
if (!promise || (typeof promise !== 'object' && typeof promise !== 'function')) {
69+
return promise;
70+
}
71+
72+
if (promise.__cloudinaryQCompatApplied) {
73+
return promise;
74+
}
75+
76+
Object.defineProperty(promise, '__cloudinaryQCompatApplied', {
77+
value: true,
78+
enumerable: false
79+
});
80+
81+
const nativeThen = promise.then;
82+
if (typeof nativeThen === 'function') {
83+
promise.then = function (...args) {
84+
return applyQCompat(nativeThen.apply(this, args));
85+
};
86+
}
87+
88+
const nativeCatch = promise.catch;
89+
if (typeof nativeCatch === 'function') {
90+
promise.catch = function (...args) {
91+
return applyQCompat(nativeCatch.apply(this, args));
92+
};
93+
}
94+
95+
const nativeFinally = promise.finally;
96+
if (typeof nativeFinally === 'function') {
97+
promise.finally = function (...args) {
98+
return applyQCompat(nativeFinally.apply(this, args));
99+
};
100+
} else {
101+
promise.finally = qFinally;
102+
}
103+
104+
if (typeof promise.fin !== 'function') {
105+
promise.fin = qFin;
106+
}
107+
108+
if (typeof promise.done !== 'function') {
109+
promise.done = qDone;
110+
}
111+
112+
if (typeof promise.nodeify !== 'function') {
113+
promise.nodeify = qNodeify;
114+
}
115+
116+
if (typeof promise.fail !== 'function') {
117+
promise.fail = qFail;
118+
}
119+
120+
if (typeof promise.progress !== 'function') {
121+
promise.progress = qProgress;
122+
}
123+
124+
if (typeof promise.spread !== 'function') {
125+
promise.spread = qSpread;
126+
}
127+
128+
return promise;
129+
}
130+
131+
module.exports = applyQCompat;

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@
1111
},
1212
"main": "cloudinary.js",
1313
"dependencies": {
14-
"lodash": "^4.17.21",
15-
"q": "^1.5.1"
14+
"lodash": "^4.17.21"
1615
},
1716
"devDependencies": {
1817
"@types/expect.js": "^0.3.29",

0 commit comments

Comments
 (0)