- Синхронность в языках программирования
- язык JavaScript
- Обратные вызовы
- Обработка ошибок при обратных вызовах
- Проблема с обратными вызовами
- Альтернативы обратным вызовам
Синхронность в языках программирования
Компьютеры асинхронны по своей конструкции.
Асинхронность означает, что все может происходить независимо от основного потока программы.
На современных компьютерах-потребителях каждая программа выполняется в течение определенного временного интервала, а затем она останавливает свое выполнение, чтобы позволить другой программе продолжить ее выполнение. Эта штука работает в цикле так быстро, что ее невозможно заметить, и мы думаем, что наши компьютеры запускают множество программ одновременно, но это иллюзия (за исключением многопроцессорных машин).
Программы внутренне используют прерывания , сигнал, который посылается процессору, чтобы привлечь внимание системы.
Я не буду вдаваться во внутренние детали этого, но просто имейте в виду, что для программ нормально быть асинхронными и останавливать их выполнение до тех пор, пока они не потребуют внимания, а компьютер тем временем может выполнять другие задачи. Когда программа ожидает ответа от сети, она не может остановить процессор до завершения запроса.
Обычно языки программирования являются синхронными, и некоторые из них предоставляют способ управления асинхронностью в языке или с помощью библиотек. C, Java, C#, PHP, Go, Ruby, Swift, Python – все они синхронны по умолчанию. Некоторые из них обрабатывают асинхронность с помощью потоков, порождая новый процесс.
язык JavaScript
JavaScript по умолчанию является синхронным и однопоточным. Это означает, что код не может создавать новые потоки и выполняться параллельно.
Строки кода выполняются последовательно, одна за другой, например:
const a = 1 const b = 2 const c = a * b console.log(c) doSomething()
Но JavaScript родился внутри браузера, его основной задачей вначале было реагировать на действия пользователя, такие как onClick , onMouseOver При наведении курсора , При обмене , при отправке и так далее. Как это можно сделать с помощью модели синхронного программирования?
Ответ был в его окружении. Браузер предоставляет способ сделать это, предоставляя набор API, которые могут обрабатывать такого рода функции.
Совсем недавно, Node.js введена неблокирующая среда ввода-вывода, чтобы распространить эту концепцию на доступ к файлам, сетевые вызовы и так далее.
Обратные вызовы
Вы не можете знать, когда пользователь собирается нажать кнопку, поэтому вы делаете следующее: вы определяете обработчик событий для события щелчка . Этот обработчик событий принимает функцию, которая будет вызвана при срабатывании события:
document.getElementById('button').addEventListener('click', () => {
//item clicked
})Это так называемый обратный вызов .
Обратный вызов – это простая функция, которая передается в качестве значения другой функции и будет выполняться только тогда, когда произойдет событие. Мы можем это сделать, потому что JavaScript имеет первоклассные функции, которые могут быть назначены переменным и переданы другим функциям (называемым функциями более высокого порядка )
Обычно весь код клиента помещается в прослушиватель событий load в объект window , который запускает функцию обратного вызова только тогда, когда страница готова:
window.addEventListener('load', () => {
//window loaded
//do what you want
})Обратные вызовы используются везде, а не только в событиях DOM.
Одним из распространенных примеров является использование таймеров:
setTimeout(() => {
// runs after 2 seconds
}, 2000)Запросы XHR также принимают обратный вызов, в этом примере назначая функцию свойству, которое будет вызываться при наступлении определенного события (в этом случае состояние запроса изменяется):
const xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
xhr.status === 200 ? console.log(xhr.responseText) : console.error('error')
}
}
xhr.open('GET', 'https://yoursite.com')
xhr.send()Обработка ошибок при обратных вызовах
Как вы справляетесь с ошибками при обратных вызовах? Одна очень распространенная стратегия состоит в том, чтобы использовать то, что Node.js принято: первым параметром в любой функции обратного вызова является объект ошибки: ошибка-первые обратные вызовы
Если ошибки нет, объект равен null . Если есть ошибка, она содержит некоторое описание ошибки и другую информацию.
fs.readFile('/file.json', (err, data) => {
if (err !== null) {
//handle error
console.log(err)
return
}
//no errors, process data
console.log(data)
})Проблема с обратными вызовами
Обратные вызовы отлично подходят для простых случаев!
Однако каждый обратный вызов добавляет уровень вложенности, и когда у вас много обратных вызовов, код начинает очень быстро усложняться:
window.addEventListener('load', () => {
document.getElementById('button').addEventListener('click', () => {
setTimeout(() => {
items.forEach(item => {
//your code here
})
}, 2000)
})
})Это всего лишь простой 4-уровневый код, но я видел гораздо больше уровней вложенности, и это не весело.
Как нам решить эту проблему?
Альтернативы обратным вызовам
Начиная с ES6, в JavaScript появилось несколько функций, которые помогают нам работать с асинхронным кодом, не связанным с использованием обратных вызовов:
- Обещания (ES2015)
- Асинхронный/Ожидание (ES2017)
Оригинал: “https://flaviocopes.com/javascript-callbacks/”