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/”