Рубрики
Без рубрики

JavaScript – от обратных вызовов до Async / ждут

JavaScript синхронно. Это означает, что он будет выполнять ваш код кода по порядку после подъема. Перед выполнением кода PROR и функциональные объявления «поднимаются» на вершину их объема. Это пример синхронного кода: Console.log (‘1’) console.log (‘2’) console.log (‘3’) это

JavaScript синхронно. Это означает, что он выполнит ваш код кода по порядку после подъемность . Перед выполнением кода var и Функция Декларации «поднимаются» на вершину их объема.

Это пример синхронного кода:

console.log('1')

console.log('2')

console.log('3')

Этот код будет надежно войти в систему «1 2 3».

Асинхронные запросы будут дождаться таймера до конца или запрос на реагирование, а остальная часть кода продолжает выполнять. Тогда, когда время правильно A Обратный вызов Будет ли весна этих асинхронных запросов в действие.

Это пример асинхронного кода:

console.log('1')

setTimeout(function afterTwoSeconds() {
  console.log('2')
}, 2000)

console.log('3')

Это на самом деле будет журнал «1 3 2», поскольку «2» находится на Setimeate который будет выполнен только на этот пример, после двух секунд. Ваше приложение не зависает в ожидании две секунды, чтобы закончить. Вместо этого он продолжает выполнять остальную часть кода, и когда ожидается, что время ожидания, он возвращается к последующим прокалам.

Вы можете спросить: «Почему это полезно?» Или «Как я могу получить мой асинхронный код, чтобы стать синхронизией?». Надеюсь, я могу показать вам ответы.

“Эта проблема”

Покажем, наша цель – искать пользователя GitHub и получить все репозитории этого пользователя. Дело в том, что мы не знаем точное имя пользователя. Поэтому мы должны перечислить всех пользователей с аналогичным именем и их соответствующими репозиториями.

Не нужно супер фантазии, что-то вроде этого

В этих примерах код запроса будет использовать XHR ( XMLHTTPREQUEST ). Вы можете заменить его с jQuery Выгодность .ajax Или более недавний родной подход называется извлекать Отказ Оба дадут вам обещания подходить из ворот.

Это будет немного изменено в зависимости от вашего подхода, но как стартер:

// url argument can be something like 'https://api.github.com/users/daspinola/repos'

function request(url) {
  const xhr = new XMLHttpRequest();
  xhr.timeout = 2000;
  xhr.onreadystatechange = function(e) {
    if (xhr.readyState === 4) {
      if (xhr.status === 200) {
       // Code here for the server answer when successful
      } else {
       // Code here for the server answer when not successful
      }
    }
  }
  xhr.ontimeout = function () {
    // Well, it took to long do some code here to handle that
  }
  xhr.open('get', url, true)
  xhr.send();
}

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

Перезвоните

Вы можете сохранить ссылку на функцию в переменной при использовании JavaScript. Затем вы можете использовать их в качестве аргументов другой функции для выполнения позже. Это наш «обратный вызов».

Одним из примеров будет:

// Execute the function "doThis" with another function as parameter, in this case "andThenThis". doThis will execute whatever code it has and when it finishes it should have "andThenThis" being executed.

doThis(andThenThis)

// Inside of "doThis" it's referenced as "callback" which is just a variable that is holding the reference to this function

function andThenThis() {
  console.log('and then this')
}

// You can name it whatever you want, "callback" is common approach

function doThis(callback) {
  console.log('this first')
  
  // the '()' is when you are telling your code to execute the function reference else it will just log the reference
  
  callback()
}

Используя Обратный вызов Решить нашу проблему позволяет нам делать что-то подобное для Запрос Функция, которую мы определены ранее:

function request(url, callback) {
  const xhr = new XMLHttpRequest();
  xhr.timeout = 2000;
  xhr.onreadystatechange = function(e) {
    if (xhr.readyState === 4) {
      if (xhr.status === 200) {
       callback(null, xhr.response)
      } else {
       callback(xhr.status, null)
      }
    }
  }
  xhr.ontimeout = function () {
   console.log('Timeout')
  }
  xhr.open('get', url, true)
  xhr.send();
}

Наша функция для запроса теперь будет принимать Перезвоните так что когда Запрос сделано его будет вызвано в случае ошибки и в случае успеха.

const userGet = `https://api.github.com/search/users?page=1&q=daspinola&type=Users`

request(userGet, function handleUsersList(error, users) {
  if (error) throw error
  const list = JSON.parse(users).items
  
  list.forEach(function(user) {
    request(user.repos_url, function handleReposList(err, repos) {
      if (err) throw err
      // Handle the repositories list here
    })
  })
})

Разбие это вниз:

  • Мы принимаем запрос, чтобы получить репозитории пользователя
  • После завершения запроса мы используем обратный вызов руля
  • Если нет ошибки, то мы разбираем наш ответ сервера в объект, используя Json.parse.
  • Затем мы повторяем наш список пользователей, поскольку он может иметь более одного Для каждого пользователя мы запрашиваем их список репозиториев. Мы будем использовать URL, который вернулся на пользователя в нашем первом ответе Мы называем repos_url. как URL для наших следующих запросов или из первого ответа
  • Когда запрос завершил обратный вызов, мы позвоним Это будет обрабатывать либо свою ошибку, либо от ответ со списком репозиториев для этого пользователя

Примечание : Отправка ошибки сначала в качестве параметра является обычной практикой, особенно при использовании Node.js.

Более «полный» и читаемый подход будет иметь некоторую обработку ошибок. Мы будем держать обратный вызов отдельно от выполнения запроса.

Что-то вроде этого:

try {
  request(userGet, handleUsersList)
} catch (e) {
  console.error('Request boom! ', e)
}

function handleUsersList(error, users) {
  if (error) throw error
  const list = JSON.parse(users).items
  
  list.forEach(function(user) {
    request(user.repos_url, handleReposList)
  })
}

function handleReposList(err, repos) {
  if (err) throw err
  
  // Handle the repositories list here
  console.log('My very few repos', repos)
}

Это заканчивается проблемами, такими как гоночные и ошибка. Racing происходит, когда вы не контролируете, какой пользователь вы получите первым. Мы запрашиваем информацию для всех из них на случай, если есть более одного. Мы не принимаем во внимание приказ. Например, пользователь 10 может прийти первым и пользователь 2 последнего. У нас есть возможное решение позже в статье.

Основная проблема с обратными вызовами заключается в том, что обслуживание и читаемость могут стать болью. Это своего рода уже есть и код практически ничего. Это известно как Обратный вызов Ад которые можно избежать с нашим следующим подходом.

Обещания

Обещания вы можете сделать ваш код более читаемым. Новый разработчик может прийти к базе кода и увидеть четкий порядок выполнения в ваш код.

Чтобы создать обещание, которое вы можете использовать:

const myPromise = new Promise(function(resolve, reject) {
  
  // code here
  
  if (codeIsFine) {
    resolve('fine')
  } else {
    reject('error')
  }
  
})

myPromise
  .then(function whenOk(response) {
    console.log(response)
    return response
  })
  .catch(function notOk(err) {
    console.error(err)
  })

Давайте разложим это:

  • Обещание инициализируется с Функция что имеет решить и Отклонить заявления
  • Сделайте свой async код внутри Обещание Функция решить Когда все происходит по желанию В противном случае отклонять
  • Когда решить найден .then Метод будет выполнен для этого Обещание Когда Отклонить найден .catch будет сработано

Вещи, которые нужно иметь в виду:

  • решить и Отклонить принимать только один параметр Решить («Yey», «работает») отправит только «Yey» на .then Функция обратного вызова
  • Если вы цепите несколько .then Добавить вернуть Если вы хотите следующий .then ценность не быть неопределенный
  • Когда Отклонить пойман с .ловить Если у вас есть .then прикованный к нему Это все равно будет выполнять это .then Вы можете увидеть .тогда Как «всегда выполняет», и вы можете проверить пример в этом комментарий
  • С цепью .then Если ошибка происходит на первом Он пропустит последующую .then пока он не найдет .ловить
  • Обещание имеет три государства в ожидании
  • В ожидании решить или Отклонить случиться разрешено отклоненный
  • Как только это в разрешено или Отклонено штат Это не может быть изменено

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

«Теория, теория, теория … Я запутался “Вы можете сказать.

Давайте будем использовать наш пример запроса с обещанием попытаться очистить вещи:

function request(url) {
  return new Promise(function (resolve, reject) {
    const xhr = new XMLHttpRequest();
    xhr.timeout = 2000;
    xhr.onreadystatechange = function(e) {
      if (xhr.readyState === 4) {
        if (xhr.status === 200) {
          resolve(xhr.response)
        } else {
          reject(xhr.status)
        }
      }
    }
    xhr.ontimeout = function () {
      reject('timeout')
    }
    xhr.open('get', url, true)
    xhr.send();
  })
}

В этом сценарии, когда вы выполняете Запрос Это вернет что-то вроде этого:

const userGet = `https://api.github.com/search/users?page=1&q=daspinola&type=Users`

const myPromise = request(userGet)

console.log('will be pending when logged', myPromise)

myPromise
  .then(function handleUsersList(users) {
    console.log('when resolve is found it comes here with the response, in this case users ', users)
    
    const list = JSON.parse(users).items
    return Promise.all(list.map(function(user) {
      return request(user.repos_url)
    }))
  })
  .then(function handleReposList(repos) {
    console.log('All users repos in an array', repos)
  })
  .catch(function handleErrors(error) {
    console.log('when a reject is executed it will come here ignoring the then statement ', error)
  })

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

Быстрое исправление было бы, чтобы разделить подобные обратные вызовы:

const userGet = `https://api.github.com/search/users?page=1&q=daspinola&type=Users`

const userRequest = request(userGet)

// Just by reading this part out loud you have a good idea of what the code does
userRequest
  .then(handleUsersList)
  .then(repoRequest)
  .then(handleReposList)
  .catch(handleErrors)
  
function handleUsersList(users) {
  return JSON.parse(users).items
}

function repoRequest(users) {
  return Promise.all(users.map(function(user) {
    return request(user.repos_url)
  }))
}

function handleReposList(repos) {
  console.log('All users repos in an array', repos)
}

function handleErrors(error) {
  console.error('Something went wrong ', error)
}

Глядя на что userRequest ждет, чтобы с .then Вы можете получить ощущение того, что мы ожидаем об этом блоке кода. Все более или менее разделено ответственностью.

Это «царапая поверхность» о том, что есть обещания. Чтобы иметь большое понимание того, как они работают, я не могу рекомендовать достаточно этого Статья Отказ

Генераторы

Другой подход – использовать генераторы. Это немного более продвигается, поэтому, если вы начинаете, не стесняйтесь прыгать на следующую тему.

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

Они представлены * в функции и выглядеть что-то вроде:

function* foo() {
  yield 1
  const args = yield 2
  console.log(args)
}
var fooIterator = foo()

console.log(fooIterator.next().value) // will log 1
console.log(fooIterator.next().value) // will log 2

fooIterator.next('aParam') // will log the console.log inside the generator 'aParam'

Вместо того, чтобы возвращаться с вернуть Генераторы имеют доходность утверждение. Он останавливает выполнение функции до A .Next сделано для этой функциональной итерации. Это похоже на .then Обещайте, что только выполняется, когда решается возвращается.

Наша функция запроса будет выглядеть так:

function request(url) {
  return function(callback) {
    const xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function(e) {
      if (xhr.readyState === 4) {
        if (xhr.status === 200) {
          callback(null, xhr.response)
        } else {
          callback(xhr.status, null)
        }
      }
    }
    xhr.ontimeout = function () {
      console.log('timeout')
    }
    xhr.open('get', url, true)
    xhr.send()
  }
}

Мы хотим иметь URL как аргумент. Но вместо того, чтобы выполнять запрос из ворот, мы хотим только тогда, когда у нас есть обратный вызов для обработки ответа.

Наше Генератор было бы что-то вроде:

function* list() {
  const userGet = `https://api.github.com/search/users?page=1&q=daspinola&type=Users`
 
  const users = yield request(userGet)
  
  yield
  
  for (let i = 0; i<=users.length; i++) {
    yield request(users[i].repos_url)
  }
}

Так и будет:

  • Подождите до первого Запрос является готовый
  • Вернуть Функция Ожидание оформления Обратный вызов Для первого Запрос Наше Запрос Функция принимает URL и возвращает Функция это ожидает, что Перезвоните
  • Ожидать Пользователи быть отправленным в следующий .следующий
  • ИТЕРТЬ ИСПОЛЬЗОВАТЬ пользователи
  • Ждать .Next Для каждого из пользователи
  • Вернуть их соответствующую функцию обратного вызова

Так что исполнение этого будет:

try {
  const iterator = list()
  iterator.next().value(function handleUsersList(err, users) {
    if (err) throw err
    const list = JSON.parse(users).items
    
    // send the list of users for the iterator
    iterator.next(list)
    
    list.forEach(function(user) {
      iterator.next().value(function userRepos(error, repos) {
        if (error) throw repos
        
        // Handle each individual user repo here
        console.log(user, JSON.parse(repos))
      })
    })
  })  
} catch (e) {
  console.error(e)
}

Мы могли бы разделить функции обратного вызова, как и мы ранее. Теперь вы получите сделку, на вынос есть, что теперь мы можем справиться с каждым отдельным списком репозитория пользователя индивидуально.

У меня смешанное чувство о генераторах. С одной стороны, я могу получить понимание того, что ожидается от кода, глядя на генератор.

Но его казнь заканчивается, имея подобные проблемы до ада обратного вызова.

Как Async/await Рекомендуется компилятор. Это потому, что он не поддерживается в более старых версиях браузера.

Также это не так распространено в моем опыте. Таким образом, он может генерировать с толку в кодовых базах, поддерживаемых различными разработчиками.

Удивительное понимание того, как работы генераторов можно найти в этом статья. А вот еще один великий Ресурс Отказ

Async/a ждать

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

sumTwentyAfterTwoSeconds(10)
  .then(result => console.log('after 2 seconds', result))
  
async function sumTwentyAfterTwoSeconds(value) {
  const remainder = afterTwoSeconds(20)
  return value + await remainder
}

function afterTwoSeconds(value) {
  return new Promise(resolve => {
    setTimeout(() => { resolve(value) }, 2000);
  });
}

В этом сценарии:

  • У нас есть SumtwentyaftertwoSeconds. как как асинхронная функция
  • Мы говорим нашему коду ждать решить или Отклонить Для нашей функции обещания послевыродоносцы
  • Это заканчивается только в .then Когда ждать Операции отделка В этом случае есть только один

Применяя это к нашему Запрос Мы оставляем это как Обещание Как видно ранее:

function request(url) {
  return new Promise(function(resolve, reject) {
    const xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function(e) {
      if (xhr.readyState === 4) {
        if (xhr.status === 200) {
          resolve(xhr.response)
        } else {
          reject(xhr.status)
        }
      }
    }
    xhr.ontimeout = function () {
      reject('timeout')
    }
    xhr.open('get', url, true)
    xhr.send()
  })
}

Мы создаем нашу async Функция с нужными ждет так:

async function list() {
  const userGet = `https://api.github.com/search/users?page=1&q=daspinola&type=Users`
  
  const users = await request(userGet)
  const usersList = JSON.parse(users).items
  
  usersList.forEach(async function (user) {
    const repos = await request(user.repos_url)
    
    handleRepoList(user, repos)
  })
}

function handleRepoList(user, repos) {
  const userRepos = JSON.parse(repos)
  
  // Handle each individual user repo here
  
  console.log(user, userRepos)
}

Так что теперь у нас есть async Список Функция, которая будет обрабатывать запросы. Другой Async нужен в Foreach так что у нас есть список Репо Для каждого пользователя для манипулирования.

Мы называем это как:

list()
  .catch(e => console.error(e))

Это и подход обещаний – мои любимые, поскольку код легко читать и изменить. Вы можете прочитать о Async/ждут больше в глубине здесь Отказ

Недостатком использования Async/a enaiq в том, что он не поддерживается на переднем углу более старых браузеров или в задней части. Вы должны использовать узел 8.

Вы можете использовать компилятор, как Бабел чтобы помочь решить это.

“Решение”

Вы можете увидеть Конец код Выполнение нашей первоначальной цели, используя Async/ждут в этом фрагменте.

Хорошо, что нужно сделать, это попробовать сами в различных формах, указанных в этой статье.

Заключение

В зависимости от сценария вы можете найти себя:

  • async/a ждать
  • обратные вызовы
  • смешивание

Это зависит от вас, что соответствует вашим целям. А также Что позволяет вам поддерживать код, так что это понятно для других и вашего будущего себя.

Примечание: Любой из подходов становятся чуть менее мягкими при использовании альтернатив для запросов, таких как Выгодность .ajax и извлекать Отказ

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

Это статья 11 из 30. Это часть проекта для публикации статьи не реже одного раза в неделю, от простых мыслей в учебные пособия. Оставьте комментарий, следуйте за мной на Diogo Spínola, а затем вернитесь к вашему блестящему проекту!

Оригинал: “https://www.freecodecamp.org/news/javascript-from-callbacks-to-async-await-1cc090ddad99/”