Рынок труда для инженеров-программистов очень конкурентен, и получение работы может зависеть от того, насколько хорошо вы пройдёте собеседование. В этой серии статей Справочник по собеседованию для инженера-программиста мы предоставим некоторые полезные знания, которые помогут вам подготовиться к следующему собеседованию по разработке программного обеспечения. В сегодняшней статье мы рассмотрим асинхронный 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

AJAX
Client --- Вызов 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 долларов США в месяц. С моей реферальной ссылкой вы получите доступ к огромному количеству знаний от тысяч писателей и присоединитесь к сообществу лидеров мнений. Улучшите свои навыки чтения и письма уже сегодня.