Вопрос об асинхронном JavaScript с Promise - PullRequest
0 голосов
/ 15 марта 2020

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

Примером ввода будет ["iliakan", "remy", "no.such.users"], а ожидаемое возвращаемое promise после разрешения даст нам [null, Object, Object], Object - данные, которые содержат информацию о пользователе.

Вот моя попытка решить этот вопрос.

function getUsers(names) {
  return new Promise(resolve => {
    const array = [];
    const url = "https://api.github.com/users/";
    const requests = names.map(name => {
      const endpoint = `${url}${name}`;
      return fetch(endpoint);
    });
    Promise.all(requests).then(reponses => {
      reponses.forEach(response => {
        if (response.status === 200) {
          response.json().then(data => {
            array.push(data);
          });
        } else {
          array.push(null);
        }
      });
      resolve(array);
    });
  });
}

Он работает, то есть возвращает массив [null, Object, Object]. И я подумал, что это соответствует требованиям, которые я изложил выше. Однако после тщательного изучения я почувствовал, что не могу в этом полностью разобраться.

Мой вопрос: посмотрите, где мы разрешаем этот массив, он разрешается сразу после forEach l * 1029. *. Одна вещь, которую я не понимаю, это то, почему она содержит все три элемента, когда некоторые элементы вставляются в нее асинхронно после завершения json(). Я имею в виду, что в случае response.status === 200 массив отправляется с данными, разрешенными из json(), и я бы предположил, что эта операция json() должна занять некоторое время. Поскольку мы не разрешили массив после завершения операции json(), почему мы все-таки получили все данные, разрешенные с json()?

 Promise.all(requests).then(reponses => {
          reponses.forEach(response => {
            if (response.status === 200) {
              response.json().then(data => {
                array.push(data); <--- this should take some time 
              });
            } else {
              array.push(null);
            }
          });
          resolve(array); <--- resolve the array immediately after the `forEach` loop
        });
      });

Мне кажется, что массив, который мы должны получить, должен только один null в нем, так как на момент его вращения .json() не должен быть закончен

Ответы [ 3 ]

2 голосов
/ 15 марта 2020

Вы правы, результат помещается позже в массив.

Попробуйте выполнить это:

const test = await getUsers(['Guerric-P']);
console.log(test.length);

Вы заметите, что он отображает 0. До того, как результат помещен в массив, его длина равна 0. Вы, вероятно, думаете, что он работает, потому что нажимаете на массив в консоли после получения результата.

Вы должны сделать что-то вроде этого:

function getUsers(names) {
    const array = [];
    const url = "https://api.github.com/users/";
    const requests = names.map(name => {
            const endpoint = `${url}${name}`;
            return fetch(endpoint);
        });
    return Promise.all(requests).then(responses => Promise.all(responses.map(x => x.status === 200 ? x.json() : null)));
};
1 голос
/ 15 марта 2020

Вам следует избегать использования конструктора Promise напрямую. Здесь нам вообще не нужно его использовать.

const url = "https://api.github.com/users/";

const getUsers = names =>
    Promise.all(names.map(name =>
        fetch(url + name).then(response =>
            response.status === 200 ? response.json() : null)));

getUsers(["iliakan", "remy", "no.such.users"]).then(console.log);

Конструктор Promise следует использовать только при создании новых типов асинхронных задач. В этом случае вам не нужно использовать конструктор Promise, поскольку fetch уже возвращает обещание.

Вам также не нужно поддерживать массив и pu sh, потому что Promise.all разрешается в массив. Наконец, вам не нужно отображать результат Promise.all. Вы можете преобразовать обещания, возвращаемые fetch.

0 голосов
/ 15 марта 2020

Дело в том, что поскольку операция json() действительно быстрая, особенно если данные ответа имеют небольшой размер, у них просто есть время для выполнения. Во-вторых, поскольку объекты в JavaScript передаются по ссылке, а не по значению, а Array является объектом в JavaScript, независимо от времени выполнения, он все равно будет sh передавать эти данные в массив, даже после того, как он будет разрешен ,

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...