Извиняюсь за повторение вопроса, но никак не могу найти ошибку в решении задачи.
Условия:
Ваш коллега-разработчик из параллельной вселенной прислал вам свою новую библиотеку для управления космическим кораблем. Т.к. космический корабль штука сложная, то и API у библиотеки довольно «развесистый», точное число методов неизвестно, документации, разумеется, нет. Зато известно, что в параллельной вселенной люди ходят по потолку, спят днём, работают ночью, а ещё используют только асинхронные функции и всегда передают callback первым аргументом. Странные ребята! У нас на Земле уже давно все на промисах пишут. Однако библиотеку нужно интегрировать в проект. Поэтому вам поступила задача написать обёртку, которая будет предоставлять тот же API, но на промисах.
Формат ввода
Пример исходного API:
const api = {
a: {
b: {
c: callback => setTimeout(() => callback(null, ’hello’), 100)
}
},
aa: {
bb: (callback, x, y) => setTimeout(() => callback(null, x + y), 200)
}
};
Формат вывода
Отправьте решение в виде:
/**
* @param {Object} api - исходное API
* @returns {Object}
*/
module.exports = function promisify(api) {
// ...
return promisedApi;
};
Пример использования:
const promisedApi = promisify(api);
promisedApi.a.b.c()
.then(res => console.log(res)); // => ’hello’
Примечания
обёртка должна возвращать rejected promise в случае ошибки при вызове исходного API, callback всегда принимает ошибку первым аргументом:
callback(error, data)
в исходном API могут встречаться константы (числа, строки и булевые), их нужно возвращать как есть:
api.foo.myConst = 1;
promisedApi.foo.myConst === 1;
инициализация обёртки должна быть «ленивой»: в исходном API может быть большое количество неймспейсов, и обращаться к ним нужно по мере использования.
Решение:
function promisify (api) {
if (!api) return null // на случай если нам ничего не пришло
const promisedApi = {}
for (const prop in api) {
if (typeof (api[prop]) === 'function') {
promisedApi[prop] = (...args) => {
return new Promise((resolve, reject) => {
api[prop]((err, result) => {
if (err) reject(err)
else resolve(result)
}, ...args)
})
}
} else if (api[prop] && typeof (api[prop]) === 'object') {
promisedApi[prop] = promisify(api[prop]) // рекурсивно обходим все вложенные свойства
} else {
promisedApi[prop] = api[prop] // статические свойства возвращаем как есть
}
}
return promisedApi
}
const api = {
foo: {
myConst: 12, p: true, c: '12dsf'
},
a: {
b: {
c: callback => setTimeout(() => callback(null, 'hello'), 100),
},
foo: {
myConst: 14, p: 2
}
},
aa: {
bb: (callback, x, y) => setTimeout(() => callback(null, x + y), 200)
}
}
const promisedApi = promisify(api)
console.log(promisedApi.foo.myConst, promisedApi.foo.p, promisedApi.foo.c) // => 12 true '12dsf'
promisedApi.a.b.c().then(res => console.log(res)).catch(err => console.error(err))// => hello
promisedApi.aa.bb(1, 2).then(res => console.log(res)).catch(err => console.error(err)) // => 3