Помогите написать асинхронную функцию

134
28 мая 2022, 18:10

Нужно написать функцию, которая получает массив всех пользователей и передает его в функцию коллбэк.

Пример использования:

getUsersInfo((users) => {
  console.log(users); // [ { name: 'Alex', age: 70 }, { name: 'Elon' } ]
});

Для получения данных вам предоставлены 2 асинхронные функции

getUsersIds - возвращает массив с идентификаторами пользователей. getUserInfo - возвращает данные пользователя по заданному идентификатору:

getUsersIds((ids) => {
  console.log(ids); // ['id2', 'id6']
});
getUserInfo('someUserId', (userInfo) => {
  console.log(userInfo); // { name: 'Alex', age: 70 }
});

Функция должна вызвать callback, переданный первым аргументом и передать туда массив данных о пользователях.

Порядок пользователей в результирующем массиве должен соответствовать порядку идентификаторов в массиве из getUsersIds

Решение должно быть следующего формата:

const { getUserInfo, getUsersIds } = db;
function getUsersInfo(onLoad) {
  // код решения
}
Answer 1

Callback-hell во всей красе. :)

// const { getUserInfo, getUsersIds } = db;
// Демо функции, чтобы работало
const data = {
  id1: {name: 'Alice',   age: 62},
  id2: {name: 'Bob',     age: 28},
  id3: {name: 'Charlie', age: 10},
};
function getUsersIds(cb) {
  setTimeout(() => cb(Object.keys(data)), 100);
};
function getUserInfo(id, cb) {
  setTimeout(() => cb(data[id]), 10*data[id].age);
}

function getUsersInfo(onLoad) {
  // Начнём с получения идентификаторов
  getUsersIds(ids => {
    // Пустой массив, если пользователей нет
    if (ids.length === 0) return onLoad([]);
    
    // Подготовим массив необходимой длины
    const users = [];
    users.length = ids.length;
    // Счётчик коллбеков, нужен чтобы среагировать на последний
    let c = ids.length;
    // Перебираем идентификаторы, запускаем «асинхронные» функции
    // for (const [i, id] of Object.entries(ids)) {
    for(let i = 0; i < ids.length; ++i) {
      const id = ids[i];
      
      getUserInfo(id, user => {
        // Кладём полученного пользователя в нужное место в массиве
        users[i] = user;
        // Когда счётчик дошёл до нуля, вызываем onLoad
        if (!--c) onLoad(users);
      });
    }
  });
}
getUsersInfo(users => console.log(JSON.stringify(users)));

А теперь попробуем это облагородить)

// const { getUserInfo, getUsersIds } = db;
// Демо функции, чтобы работало
const getUsersIds = (cb) => cb([1, 2, 3]);
const getUserInfo = (id, cb) => cb({1: {name: 'Alice'}, 2: {name: 'Bob'}, 3: {name: 'Charlie'}}[id]);
// Функция, улучшающая интерфейс предоставленных функций
const promisify = fn => (...args) => 
    new Promise((resolve) => fn(...args, res => resolve(res)));
let getUsersIds$ = promisify(getUsersIds);
const getUserInfo$ = promisify(getUserInfo);
// Promise-реализация, чтобы не менять интерфейс функции, необходимой по ТЗ
async function getUsersInfo$() {
  const ids = await getUsersIds$();
  return await Promise.all(ids.map(id => getUserInfo$(id)));
}
function getUsersInfo(onLoad) {
  getUsersInfo$().then(onLoad)
}
// Вызов функций
getUsersInfo(users => console.log(JSON.stringify(users)));
setTimeout(() => {
  getUsersIds$ = () => Promise.resolve([]);
  getUsersInfo(users => console.log(JSON.stringify(users))); // []
}, 1000);

READ ALSO
прерывается выполнения скрипта, без ошибки

прерывается выполнения скрипта, без ошибки

Есть несколько асинхронных функций которые вызываются вот так:

276
Сохранение передаваемых params во vueRouter

Сохранение передаваемых params во vueRouter

Есть динамический роут, который выводит отфильтрованный список товаров, фильтрация происходит в getter во vuex, все бы ничего, но если перезагрузить...

135