Где ошибка и где можно рациональнее написать код, чтобы сократить время обработки?
тут долгая и «дорогая» задача — нахождение суммы делителей числа. У вас в решении для одного (любого) числа она вычисляется по многу раз. Этого нужно избежать.

Составьте словарь: объект или Map, где ключ – сумма делителей. А значение – массив чисел, у которых оказалась такая сумма делителей.

Один раз пройдитесь по диапазону 1 ... 3000 и заполните словарь, посчитав сумму делителей для каждого по одному разу.

Затем остаётся пройти по словарю и набрать из каждого из массивов значений, пары.  Например, все простые числа свалятся в массив под ключом 1 (все делятся только на 1 и на себя). {1: [1, 2, 3, 5, 7, 11, 13, ... ], ... } Из этого массива нужно все возможные пары повытаскивать: [1, 2], [1, 3], [1, 5], ..., [2, 3], [2, 5], ...

Ещё оптимизация: при поиске делителей числа есть смысл проверять не до самого числа, а только до его половины. Например, 21:  проверить, делится, ли на 2, 3, 4, ... 10 <= (21 / 2 = 10.5) и дальше проверять не нужно.
Зачем внутри getDivisors создавать массив, а потом писать отдельную ф-ю для складывания массива? Можно было сразу складывать в результат и возвращать сумму.
По поводу использования i - вы не объявляли её внутри for, поэтому в нестрогом режиме она стала глобальной. Если объявлять внутри
for (let i = 0; i < arr.length; i ++)
то она будет локализована внутри цикла и проблем в других местах не будет.
Для продвинутой работы с массивами, рекомендую ознакомиться с функциями reduce и map