Skip to content

Commit e8bd2fd

Browse files
UTAPI-116: Fix node22 warning DEP0174 promisify
It floods the stderr logs of cloudserver and utapi ``` (node:4634) [DEP0174] DeprecationWarning: Calling promisify on a function that returns a Promise is likely a mistake. ```
1 parent ba38c86 commit e8bd2fd

File tree

1 file changed

+47
-2
lines changed

1 file changed

+47
-2
lines changed

libV2/redis.js

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
const EventEmitter = require('events');
2-
const { callbackify, promisify } = require('util');
2+
const { callbackify } = require('util');
33
const IORedis = require('ioredis');
44
const { jsutil } = require('arsenal');
55
const BackOff = require('backo');
@@ -15,6 +15,49 @@ const moduleLogger = new LoggerContext({
1515
const COMMAND_TIMEOUT = 10000;
1616
const CONNECTION_TIMEOUT = 30000;
1717

18+
/**
19+
* Promisifies a function. If the function already returns a promise,
20+
* it returns it as is. Handles both callbacks and promises.
21+
*
22+
* @param {Function} originalFn The function to promisify.
23+
* @returns {Function} A function that returns a promise.
24+
*/
25+
function flexiblePromisify(originalFn) {
26+
// Check if the function provides a custom promise-based version.
27+
// This is the same mechanism Node's util.promisify uses.
28+
const customPromisified = originalFn[Symbol.for('nodejs.util.promisify.custom')];
29+
if (typeof customPromisified === 'function') {
30+
return customPromisified;
31+
}
32+
33+
// Return the new promise-based wrapper function.
34+
return function flexiblePromisifiedWrapper(...args) {
35+
const thisCtx = this;
36+
37+
return new Promise((resolve, reject) => {
38+
// 1. Callback will be used if `originalFn` is a callback-style function.
39+
function callback(err, result) {
40+
if (err) {
41+
return reject(err);
42+
}
43+
return resolve(result);
44+
}
45+
46+
// 2. Call the originalFn, with user's args AND our custom callback.
47+
const potentialPromise = originalFn.apply(thisCtx, [...args, callback]);
48+
49+
// 3. If originalFn returned a promise, we use its result and ignore the callback.
50+
if (potentialPromise && typeof potentialPromise.then === 'function') {
51+
// The function returned a promise. We'll trust it as the source of truth.
52+
potentialPromise.then(resolve, reject);
53+
}
54+
55+
// If the function did NOT return a promise (i.e., it's a standard callback function),
56+
// then our promise is already wired up to be resolved or rejected by the `callback` we passed in.
57+
});
58+
};
59+
}
60+
1861
/**
1962
* Creates a new Redis client instance
2063
* @param {object} conf - redis configuration
@@ -201,7 +244,9 @@ class RedisClient extends EventEmitter {
201244
if (callback !== undefined) {
202245
// If a callback is provided `func` is assumed to also take a callback
203246
// and is converted to a promise using promisify
204-
return callbackify(this._call.bind(this))(promisify(func), callback);
247+
// Note (DEP0174): flexiblePromisify avoids promisifying a function that already returns a promise
248+
// With redisClientV2 func returns a promise even if there is a callback
249+
return callbackify(this._call.bind(this))(flexiblePromisify(func), callback);
205250
}
206251
return this._call(func);
207252
}

0 commit comments

Comments
 (0)