Задачка на js (асинхронная загрузка)

Подскажите куда копать для решения задачки, параметр done реализовывать не нужно, просто вызвать.

function asyncLoad(ids, load, done) {
// asyncLoad takes an array of identifiers, load function and done function.
//
// load function knows how to load stuff. It takes an identifier
// and a callback function which will be called with load result
//
// done function should be called only when all work of loading stuff is done.
// It takes an array of loaded items
//
// * loaded items should be the same order as ids
// * load should be performed in parallel
}

users = [
{ id: 0, name: ‘Oluwafemi Enu’ },
{ id: 1, name: ‘Kgosi Ekene’ },
{ id: 2, name: ‘Olufemi Berhanu’ },
{ id: 3, name: ‘Farai Wasswa’ },
{ id: 4, name: ‘Eseoghene Chima’ },
{ id: 5, name: ‘Limbani Sefu’ },
{ id: 6, name: ‘Tiyamike Itumeleng’ },
{ id: 7, name: ‘Umukoro Oghenekaro’ },
{ id: 8, name: ‘Rudo Dalitso’ },
{ id: 9, name: ‘Chioma Olufemi’ },
{ id: 10, name: ‘Ekwueme Okoro’ }
],

Классика.
Если писать самостоятельно без библиотек, то надо реализовать такую констуркцию, которая

  1. Инициализирует загрузку всех элементов.
  2. Запоминает сколько элементов на загрузку было инициализировано.
  3. При каждом ответе устанавливает флаг для загруженного элемента, и проверяет все ли флаги установлены (т.е. все ли элементы из ids загружены). Если загружены все, вызывает done.

И на промисах http://www.html5rocks.com/en/tutorials/es6/promises/ а вернее на реализации промисов из jQuery

function asyncLoad(ids, load, done) {
	return $.when.apply($, ids.map(function (id) {
		var d = new $.Deferred();
		load(id, d.resolve);
		return d;
	})).then(done);
}
  1. $.when вернет такой промис, который вычислится в значение, когда все промисы-аргументы $.when вычислятся в значение. Не принимает массива аргументом, поэтому вызываем через apply.
  2. ids.map вернет массив промисов, каждый из который вычислиться в значение, когда произойдет загрузка ресурса по айдишнику

Это как то можно на чистом JS решить, что то тяжело понимается.

function asyncLoad(ids, load, done) {
  var loadingState = [];
  // Инициализируем загрузку всех элементов
  ids.forEach(function (id, index) {
    loadingState[index] = false; // Запоминает сколько элементов на загрузку было инициализировано.
    // Инициализируем загрузку элемента
    load(id, function () {
      loadingState[index] = true; // При каждом ответе устанавливает флаг для загруженного элемента,
      // проверяет все ли флаги установлены (т.е. все ли элементы из ids загружены).
      // если не все флаги говорят о том, что данные загружены, выходим из функции
      for (var i = 0; i < loadingState.length; i += 1) {
        if (!loadingState[i]) {
          return;
        }
      }
      // Если загружены все данные, вызывает done.
      done();
    });
  });
}
1 лайк

Еще подсказали, актуально решение выше к сказанному ниже ?
По поводу asyncLoad.
Чтобы понять что она делает можно представить себе такую задачу. Есть сервис, который по id отдает данные пользователя.

Сразу нужно задать себе вопрос: могу ли я написать такую функцию, которая будучи вызванной, например с id юзера, вернет мне данные этого юзера. Ответ - нет. Я не могу написать функцию которая мне вернет юзера. Почему? Потому что запрос к серверу выполняется асинхронно, и ответ мы получим не сразу, а в какой-то момент времени.

Спрашиваем себя дальше. Каже же мне все-таки получить этого юзера. Ответ: Ну когда я получу данные от сервера я могу вызвать какую-то функцию с этими данными.

Тут рождается функция, которая в какой-то момент времени вызовет callback с данными юзера

function load(userId, callback) {


callback(user);

}

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

asyncLoad(ids, load, allLoaded)

Идентификаторы (массив) передаются первым параметром, функция для загрузки одной записи - вторым, и третьим параметром - функция, которая должна быть вызвана с коллекцией данных, когда они будут загружены. Опять же ее передают извне, вам не нужно заботиться о ее реализации.

Когда нужно получить коллекцию данных, каждый элемент которой мы получаем асинхронно, можно задать тот же самый вопрос. Могу ли я написать функцию, которая сможет вернуть такую коллекцию данных? Ответ - нет. Что я могу сделать? Передать ей коллбек, который вызвать как только все данные будут загружены. Это ваш allLoaded

1 лайк

Есть у меня такой тест для но он не срабатывает используя функцию которую Ты написал, в чем тут загвоздка ?
describe(‘asyncLoad’, function() {
var userLoadTime = 1000,

users = [
{ id: 0, name: ‘Oluwafemi Enu’ },
{ id: 1, name: ‘Kgosi Ekene’ },
{ id: 2, name: ‘Olufemi Berhanu’ },
{ id: 3, name: ‘Farai Wasswa’ },
{ id: 4, name: ‘Eseoghene Chima’ },
{ id: 5, name: ‘Limbani Sefu’ },
{ id: 6, name: ‘Tiyamike Itumeleng’ },
{ id: 7, name: ‘Umukoro Oghenekaro’ },
{ id: 8, name: ‘Rudo Dalitso’ },
{ id: 9, name: ‘Chioma Olufemi’ },
{ id: 10, name: ‘Ekwueme Okoro’ }
],

userIds = users.map(function(u) { return u.id });

it(‘takes load and done functions and calls done with an array of loaded items’, function(done) {
var load = function(id, fn) {
// Emulate users async load
// Load each user within userLoadTime (1 second)

  setTimeout(function() {
    var match = users.filter(function(user) { return user.id === id });
    match.length ? fn(match[0]) : fn(null);
  }, Math.floor(Math.random() * userLoadTime));
},

allLoaded = function(loadedUsers) {
  // Define all users loaded callback function
  // which takes loaded users array

  expect(loadedUsers).toEqual(users);
  done();
};

asyncLoad(userIds, load, allLoaded);

}, userLoadTime);

Функция load, похоже ожидает аргументом айди пользователя, а не объект. Значит сперва надо поменять load(id, function () { на load(id.id, function () {.

Доводить свою функцию до состояния, пока она проходит тесты я не буду. В моей функции происходит в первую очередь демонстрация принципов. Тебе надо понять суть происходящего и написать свое решение, которое уже точно будет влезать в условия изначальной задачи.

В моем случае есть не понимание происходящего, из - за этого и ступор. ( Отложу решение может потом прийдет озарение.

На каком этапе не понятно.

Сначала словесное описание:

Все ли пункты ясны. Потом устанавливаем соответствие между описанием и кодом:

function asyncLoad(ids, load, done) {
  var loadingState = [];
  // Инициализируем загрузку всех элементов
  ids.forEach(function (id, index) {
    loadingState[index] = false; // Запоминает сколько элементов на загрузку было инициализировано.
    // Инициализируем загрузку элемента
    load(id, function () {
      loadingState[index] = true; // При каждом ответе устанавливает флаг для загруженного элемента,
      // проверяет все ли флаги установлены (т.е. все ли элементы из ids загружены).
      // если не все флаги говорят о том, что данные загружены, выходим из функции
      for (var i = 0; i < loadingState.length; i += 1) {
        if (!loadingState[i]) {
          return;
        }
      }
      // Если загружены все данные, вызывает done.
      done();
    });
  });
}

Какая часть или принцип работы кода не ясны?

В твоем писании все понятно, но есть нюанс который хочу уточнить. “load()” и его callback(). Следующий load() может запуститься, пока еще отрабатывает callback() предыдущего, и что делает “load()” нам особо не важно ? И что в asyncLoad функции асинхронного, для меня загадка ?

Именно так. Считай, что между вызовом load и вызовом его callback может пройти любое случайное количество времени. И точно такая же картина (вызов load синхронный со всем кодом, и последующий вызов callback) вырисовывается для каждого загружаемого id.

Воспринимай коллбеки, как функции, которые будут вызваны в будущем, асинхронно по отношению к текущему коду.

Асинхронны вызовы коллбеков функции load.

Кстати, внеше функция asyncLoad выглядит точно так же, как и load: мы ей передаем аргументы, и через некоторое время вызывается аргумент-функция (кстати, вот хорошее рассуждение о коллбеках, как о передаче управления в потоке выполнения http://forum.jscourse.com/t/24-zadanie-ajax/825/36?u=dmitry)

Поправьте меня, если я ошибаюсь.
Суть и преимущество асинхронных вызовов (перед синхронными) в том, что они могут не выполняться (error, ajaxError).
В 24 задании, если картинка не загрузилась, мы (я) пробовали загрузить следующую и работать дальше.

Тут же стоит задача реализовать несколько асинхронных вызовов и в случае успешности, вызвать определенную функцию. Тут и конфликт. Мы не должны ставить себя в рамки ожидания одного из ответов, притом, почему-то с заранее известным результатом.
Тут еще нарушается принцип связанности данных - если мы запрашиваем список определенных людей, входящих в группу, то мы не должны запрашивать каждого в отдельности (это работа БД).
Используйте синхронный вызов, или запрашивайте все данные разом.

Либо как вариант, использовать пустой массив, в который при посылке запроса (beforeSend, ajaxSend) пушить данные (любые), а при завершении запроса с любым результатом (complete, ajaxComplete) - попить данные.
И когда массив становится пустым, выполнять функцию done

В качестве примера из жизни, демонстрирующего мою позицию: Загрузка компьютера. Мы не ждем когда все компоненты отработают правильно. Достаточно основных. Если компонент не отработал, но уже в системе, мы будем запускать его (решать причину неудачного запуска).

Для синхронных точно так же можно выполнить код внутри try-catch, и тогда они могут спокойно выполнится. Преимущество асинхронных запросов перед синхронными в том, что можно получить данные, не блокируя интерфейс (вернее основной поток выполнения программы).

для работы с API это распространенный сценарий.

Не надо, так как страдает производительность клиентов.

Спасибо, Дима очень помог я разобрался и довел до ума задачу.