Рынок труда для инженеров-программистов очень конкурентен, и получение работы может зависеть от того, насколько хорошо вы пройдёте собеседование. В этой серии статей Справочник по собеседованию для инженера-программиста мы предоставим некоторые полезные знания, которые помогут вам подготовиться к следующему собеседованию по разработке программного обеспечения. В сегодняшней статье мы рассмотрим асинхронный JavaScript. Давай начнем!
Основы
Асинхронный Javascript построен на системе, основанной на событиях, где каждое промис и наблюдаемая библиотека функционируют через события. С другой стороны, синхронный код использует стек вызовов, представляющий собой структуру данных, которая отслеживает выполнение программы.
В отличие от синхронного кода, асинхронный код использует очередь событий, которая представляет собой список функций, обрабатываемых в последовательности «первым поступил — первым обслужен» (FIFO). Эта очередь событий неоднократно отслеживается циклом событий, который представляет собой программную конструкцию, передающую функции из очереди событий в стек вызовов для выполнения.
setTimeout(() => { console.log("I will happen later..."); }, 5000);
Например, функция setTimeout не выполняется немедленно через 5 секунд, а вместо этого добавляется в очередь событий и ждет 5 секунд, прежде чем будет выполнена циклом обработки событий.
Важно отметить, что сам по себе Javascript на самом деле не является асинхронным. Это среда, в которой работает Javascript, например браузеры и Node.js, которая обеспечивает асинхронные возможности, в частности, через объектную модель поведения (BOM). Примеры функций спецификации включают в себя:
Объектная модель документа (DOM)
- получитьэлементбиид
Объектная модель поведения (BOM)
- setTimeout
- XMLHttpRequest
- принести
- история
ECMAScript
- Объект
- Множество
- Функция
Асинхронный/ожидание
Async/Await позволяет нам писать асинхронный код, как если бы он был синхронным. Он делает это, используя генераторы и операторы yield как способ «приостановить» выполнение.
С помощью асинхронных функций мы можем извлечь значение обещания, даже если оно находится внутри функции обратного вызова, что позволяет нам присвоить его переменной. По сути, async/await — это просто синтаксис, благодаря которому асинхронный код выглядит синхронно написанным.
async function foo() { try{ let res = await fetch("https://jsonplaceholder.typicode.com/todos"); let data = await res.json() console.log(data); } catch (err){ console.log(err) } }; foo(); // Fetch API comparison function foo() { fetch("https://jsonplaceholder.typicode.com/todos") .then(res => res.json()) .then(data => console.log(data)) .catch(err => console.log(err)); }
Как это выглядит за кадром:
const request = url => { ajax("GET", url).then(response => it.next(JSON.parse(response))); }; function* main() { const result = yield fetch("http://api.com/users/1"); const data = yield result.json(); console.log(data); } const it = main(); it.next();
Другой пример:
async function getDataOne() { let res = await fetch("https://jsonplaceholder.typicode.com/todos/1"); let data = await res.json() return data; } async function getDataTwo() { let res = await fetch("https://jsonplaceholder.typicode.com/todos/2"); let data = await res.json() return data; } async function getBoth() { var resultOne = await getDataOne(); var resultTwo = await getDataTwo(); console.log(resultOne, resultTwo) } getBoth(); // Compared to this function foo() { fetch("https://jsonplaceholder.typicode.com/todos/1") .then(res => res.json()) .then(resultOne => { fetch("https://jsonplaceholder.typicode.com/todos/2") .then(res => res.json()) .then(resultTwo => { console.log(resultOne, resultTwo) }) .catch(err => console.log(err)); }) .catch(err => console.log(err)); } foo()
Получить API
Библиотека AJAX на основе Promise включена по умолчанию во все современные браузеры и называется Fetch. Обещание выступает в качестве заполнителя для будущего ответа.
Fetch всегда возвращает обещание, которое при разрешении даст объект ответа. Этот объект ответа включает в себя различные вспомогательные методы, такие как response.json()
, response.text()
и response.blob()
и другие.
Кроме того, Fetch принимает второй параметр, который представляет собой объект конфигурации, позволяющий изменять поведение запроса.
ПОЛУЧАТЬ
// ES6 fetch("http://www.api.com/data") .then(res => res.json()) .then(data => data) .catch(err => err);
ПОЧТА
// ES6 fetch("http://www.api.com/data", { method: "POST", headers: { Accept: "application/json", "Content-type": "application/json" }, body: JSON.stringify({ title: title, body: body }) }) .then(res => res.json()) .then(data => { console.log(data) }) .catch(err => err); // Async Await async function postData(){ let response = await fetch("http://www.api.com/data", { method: "POST", headers: { Accept: "application/json", "Content-type": "application/json" }, body: JSON.stringify({ title: title, body: body }) }) let data = await response.json() if (response.status !== 2000){ throw Error(data.message) } console.log(data) }
Обещания
Конструктор Promise()
принимает функцию, которая принимает функции resolve
и reject
.
new Promise((resolve, reject) => { if (goodThingsHappen) { resolve(goodThings); // passed to .then() } else { reject(reasonsForFailing); } });
Промисы используются для отложенных и асинхронных операций и представляют будущий результат, который еще предстоит определить.
Обещания получаются с помощью метода .then()
.
// Immediately resolved. let myPromise = Promise.resolve("Foo"); myPromise.then((res) => console.log(res);) // Foo // Delayed var myPromise = new Promise(function(resolve, reject){ setTimeout(() => resolve(4), 2000); // Return 4 after 2 seconds. }); myPromise.then((res) => { res += 3; // Wait for the 4 and add 3 to it. console.log(res); // 7 after 2 seconds. });
За кулисами Fetch с обещаниями
function fetch(method, url) { return new Promise(function(resolve, reject) { let xhr = new XMLHttpRequest(); xhr.open(method, url); xhr.onload = function() { if (this.status >= 200 && this.staus <= 300) { resolve(xhr.response); } else { reject({ status: this.status, statusText: xhr.statusText }); } }; xhr.onerror = function() { reject({ status: this.status, statusText: xhr.statusText }); }; xhr.send(); }); } fetch("GET", "http://jsonplaceholder.typicode.com/todos") .then(function(data) { let todos = JSON.parse(data); let output = ""; for (let todo of todos) { output += ` <li> <h3>${todo.title}</h3> <p>${todo.item}</p> `; } document.getElementById("list").innerHTML = output; }) .catch(function(err) { console.log(err); });
Обещай все
Принимает массив промисов и выполняет, если все они успешны.
Promise.all([ fetch("/api/endpoint"), fetch("/api/another-endpoint"), fetch("/api/yet-another-endpoint") ]) .then(responses => { // array of responses console.log(responses[0]); // users console.log(responses[1]); // products console.log(responses[2]); // clients }) .catch(err => { console.log(err); });
Цепочка
let futureNumber = Promise.resolve(2); // 2 futureNumber .then(n => n + 1) // 3 .then(n => n * 2) // 6 .then(n => Math.pow(n, 2)) // 36 .then(n => console.log(n)); // 36 futureNumber.then(n => console.log(n)); //2
Обратные вызовы
Обратный вызов — это функция, которая выполняется после завершения выполнения другой функции, отсюда и название «обратный вызов».
В JavaScript функции считаются объектами, что позволяет им принимать функции в качестве аргументов и возвращаться другими функциями. Эти функции называются функциями высшего порядка. Любая функция, которая передается в качестве аргумента, называется функцией обратного вызова.
function foo(input, callback) { console.log(input); callback(); } // Anonymous callback. Logs Foo Bar. foo("Foo", function() { console.log("Bar"); });
Передача именованных функций.
function baz(input) { console.log(input); } // This executes the baz function immediately. // Logs Baz Foo and "TypeError: callback is not a function foo("Foo", baz("Baz")); // Named callback. Logs Foo Baz. foo("Foo", function() { baz("Baz"); }); // Works fine because it doesn't execute immediately. // Must have no arguments. Logs Foo Qux. foo("Foo", logQux); function logQux() { console.log("Qux"); }
Другой пример:
function callbackSandwich(callbackFunction) { console.log("Top piece of bread."); callbackFunction(); console.log("Bottom piece of bread."); } // We pass in an anonymous function, to be called inside. callbackSandwich(function() { console.log("Slice of cheese."); });
AJAX с обратным вызовом
var request = new XMLHttpRequest(); request.addEventListener("load", event => { console.log(event.target.responseText); }); request.open("GET", "http://www.api.com/data"); request.send();
Код рефакторинга:
function ajax(method, url, callback) { var request = new XMLHttpRequest(); request.addEventListener("load", callback); request.open(method, url); request.send(); } ajax("GET", "http://www.api.com/data", event => { console.log("SUCCESS", event.target.responseText); });
АЯКС
var xhr = new XMLHttpRequest(); xhr.open("GET", "http://www.api.com/data", true); xhr.onload = function() { if (this.status == 200) { return JSON.parse(this.responseText); } }; xhr.onerror = function() { console.log("error"); }; xhr.send();
Альтернатива:
var request = new XMLHttpRequest(); request.addEventListener("load", event => { console.log(event.target.responseText); }); request.addEventListener("error", event => { console.error(event.target.responseText); }); request.open("GET", "http://www.api.com/data"); request.send();
Обычный HTTP-запросClient
--- Запрос ---› Server
Client
‹--- Ответ --- Server
AJAXClient
--- Вызов JS ---› Ajax Engine
--- XMLHttpRequest (XHR) ---› Server
Client
‹--- HTML --- Ajax Engine
‹ --- JSON --- Server
Объект XMLHttpRequest (XHR)
Среда Javascript браузера предоставляет API в виде объекта, который имеет свои свойства и методы для передачи данных между браузером и сервером.
// Create XHR Object var xhr = new XMLHttpRequest(); // Can be named req // Establish server connection - type, url/file, async xhr.open("GET", "http://www.api.com/data", true); // readyState 1 xhr.onload = function() { // readyState 1, 4 if (this.status == 200) { return JSON.parse(this.responseText); } }; // If it fails xhr.onerror = function() { console.log("error"); }; // Send request xhr.send();
Загрузка
xhr.onprogress = function() { // readyState 3 // Show loading... };
ПОЧТА
<form method="POST" id="form"> <input type="text" id="input"> <input type="submit" value="submit"> </form> document.getElementById("form").addEventListener("submit", submitForm); function submitForm(e) { e.preventDefault(); var input = document.getElementById("input").value; var params = "input=" + input; var xhr = new XMLHttpRequest(); xhr.open("POST", "http://www.api.com/data", true); xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); xhr.onload = function() { // readyState 1, 4 if (this.status == 200) { return JSON.parse(this.responseText); } }; xhr.onerror = function() { console.log("error"); }; xhr.send(params); }
В следующей части JavaScript мы представим концепции объектно-ориентированного программирования (ООП). Следите за обновлениями!
Заключительные слова
Спасибо, что нашли время прочитать эту статью. Если вы нашли это информативным и полезным, пожалуйста, поддержите меня, подписавшись на мою страницу и похлопав ее. Ваше участие побудит меня продолжать создавать для вас ценный контент.
Чтобы перейти на новый уровень, подумайте о том, чтобы стать участником Medium всего за 5 долларов США в месяц. С моей реферальной ссылкой вы получите доступ к огромному количеству знаний от тысяч писателей и присоединитесь к сообществу лидеров мнений. Улучшите свои навыки чтения и письма уже сегодня.