Как работает асинхронный JavaScript

В этом руководстве вы узнаете все об асинхронном JavaScript.

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

Что такое синхронный код?

Когда мы пишем приложение на JavaScript, она выполняется строка за строкой. Когда строка выполнена полностью, код перемещается вперед для выполнения следующей строки только тогда и тогда.

Давайте рассмотрим пример этого:

let greet_one = "Hello"
let greet_two = "World!!!"
console.log(greet_one)
for(let i=0;i<1000000000;i++){
}
console.log(greet_two);

Теперь, если вы запустите приведенный выше пример на своей машине, вы это заметите greet_one сначала журналы. Затем программа ждет несколько секунд, а затем регистрируется greet_two. Это потому, что код выполняется строка за строкой. Это называется синхронным кодом.

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

Что такое асинхронный код?

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

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

let greet_one = "Hello"
let greet_two = "World!!!"
console.log(greet_one)
setTimeout(function(){
    console.log("Asynchronous");
}, 10000)
console.log(greet_two);

В приведенном выше примере, если вы запустите код на своей машине, вы увидите greet_one и greet_two зарегистрирован, даже если между этими двумя журналами есть код.

Теперь setTimeout асинхронный, поэтому он работает в фоновом режиме, позволяя коду после него выполняться во время работы. Через 10 секунд Asynchronous будет напечатана, поскольку мы установили время в 10 секунд в setTimeout, чтобы выполнить его через 10 секунд.

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

«Функция обратного вызова – это функция, которая передается другой функции как аргумент, который затем вызывается внутри внешней функции для выполнения определенной процедуры или действия». (MDN)

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

function compute(action, x, y){
    if(action === "add"){
        return x+y
    }else if(action === "divide"){
        return x/y
    }
}

console.log(compute("add",10,5))   
console.log(compute("divide",10,5)) 

В вышеприведенном примере мы имеем две операции. Но что если мы хотим добавить больше операций? Тогда количество операторов if/else увеличится. Этот код будет длинным, поэтому вместо него мы используем обратные вызовы:

function add(x,y){
    return x+y
}

function divide(x,y){
    return x/y
}

function compute(callBack, x, y){
    return callBack(x,y)
}

console.log(compute(add, 10, 5))    // 2
console.log(compute(divide, 10, 5))

Теперь, когда мы звоним compute с тремя аргументами, один из которых является операцией. Когда мы входим в функцию compute, функция возвращает функцию с заданным названием действия. В ответ он вызывает эту функцию и возвращает результат.

Добро пожаловать в Callback Hell

Обратные вызовы – это отлично, но вы не хотите использовать их чрезмерно. Если вы это сделаете, вы получите нечто, называемое «ад обратного вызова». Это случается, когда вы вкладываете обратные вызовы в обратные вызовы на многих уровнях.

Форма обратного ада напоминает пирамиду, ее также называют «пирамидой гибели». Это делает код очень сложным для поддержки и понимания. Вот пример ада обратного вызова:

setTimeout(() =>{
    console.log("One Second");
    setTimeout(() =>{
        console.log("Two Seconds");
        setTimeout(() =>{
            console.log("Three Seconds");
            setTimeout(() =>{
                console.log("Four Seconds");
                setTimeout(() =>{
                    console.log("Five Seconds");
                }, 1000)
            }, 1000)
        }, 1000)
    }, 1000)
}, 1000)

Когда пройдет одна секунда, код регистрирует одну секунду. Затем есть еще один звонок, который выполняется спустя еще одну секунду и регистрируется «две секунды», и он продолжается и продолжается.

Мы можем избежать этого ада обратного вызова, используя что-то вызов Promises в асинхронном JavaScript.

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

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

Прежде чем показать вам, как обещания работают через код, я объясню обещания с помощью аналогии с лотерейным билетом.

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

Теперь давайте рассмотрим пример кода:

const request = fetch('
console.log(request);

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

1212

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

Как потреблять обещания

Мы можем использовать обещание с помощью метода then() в обещании. Производственный код – это код, для завершения которого может потребоваться некоторое время. Потребляющий код – это код, который должен ждать результата.

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

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

const request = fetch('.then((response) =>{
    console.log(response);
    return response.json()
}).then((data) =>{
    console.log(data);
})

Делаем запрос в API страны. Затем, после запроса на получение, мы используем then() способ потребления обещания. После этого мы возвращаем кучу информации, например заголовок, статус и т.д. (вы можете увидеть это на картинке ниже).

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

Чтобы выполнить это обещание, мы снова используем метод then() для регистрации данных из ответа. Использование нескольких then() методов по одному запросу вызывается цепочка обещаний.

121212

Как работать с отклоненными обещаниями

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

Итак, возьмем пример: мы разместим наши обещания в функции под названием call(). В HTML мы создадим кнопку и добавим в нее слушателя событий. Когда мы нажимаем кнопку, она вызовет call() функция.

Вот как это выглядит:

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Promises</title>
</head>
<body>
    
    <button class="btn">Request</button>
    <script src="
</body>
</html>

script.js:

function call(){

    const request = fetch('.then((response) =>{
        console.log(response);
        return response.json()
    }).then((data) =>{
        console.log(data);
    })

}

const btn = document.querySelector("button")
btn.addEventListener("click", function(){
    call();
})

Почему мы это делаем? Мы даем обещание получить отказ. При запуске этого кода перейдите к проверке и выберите вкладку сети. Установите для параметра «Без ограничения» значение «оффлайн» и нажмите кнопку для отправки запроса. Вы получите отклоненное обещание.

1111

После того, как мы нажмем кнопку, мы получим ошибку, вызванную отсутствием подключения к Интернету.

Такая ситуация может произойти в реальном мире, если Интернет-соединение пользователя медленное. Делаем API-запрос, для которого нам нужен интернет с солидной скоростью. Иногда у клиента могут возникнуть проблемы с Интернетом. Это может привести к отклоненным обещаниям, что приведет к ошибке, которую мы еще не знаем, как обрабатывать.

Сейчас мы научимся справляться с этой ошибкой. Мы использовали then() для использования наших обещаний. Подобно этому мы создадим цепь catch() метод этого обещания. Посмотрите на следующий код:

function call(){

    const request = fetch('.then((response) =>{
        console.log(response);
        return response.json()
    }).then((data) =>{
        console.log(data);
    }).catch((err) =>{
        alert(err);
    })

}

const btn = document.querySelector("button")
btn.addEventListener("click", function(){
    call();
})

Теперь catch() метод получит сообщение об ошибке через отклоненное обещание и отобразит сообщение в уведомлении.

Мы получаем ошибку, поскольку мы получили отклоненное обещание, указывающее на наличие определенной проблемы. Мы можем делать все, что угодно, в блоке catch(), когда мы сталкиваемся с ошибкой.

Кроме метода catch(), существует еще один полезный метод, вызываемый finally(). Мы можем привязать его к обещаниям, которые будут выполняться независимо от того, принято или отклонено обещание.

function call(){

    const request = fetch('.then((response) =>{
        console.log(response);
        return response.json()
    }).then((data) =>{
        console.log(data);
    }).catch((err) =>{
        console.log(err);
    }).finally(() =>{
        console.log("Will always run");
    })

}

const btn = document.querySelector("button")
btn.addEventListener("click", function(){
    call();
})

Мы можем этим воспользоваться finally() метод очищения вещей после вызова API. Есть много способов использования finally() метод.

Как создать обещание

Мы знаем, как потреблять обещания, но как насчет создания собственных обещаний? Вы можете сделать это с помощью new Promise().

Вы можете спросить – зачем нам собственные обещания? Во-первых, обещания носят асинхронный характер. Мы можем создать асинхронную любую задачу, создав собственные обещания. Мы можем справиться с ними с помощью then() и catch() методы, о которых мы узнали в вышеприведенном разделе.

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

Давайте посмотрим, как это работает на примере:

let lottery = new Promise(function(resolve, reject){
    console.log("Lottery is happening");

    setTimeout(() => {
        if(Math.random() >= 0.5){
            resolve("You Won!!!")
        }
        else{
            reject(new Error("Better luck next time"))
        }
    }, 5000);

})

Сначала мы создали обещание с помощью new Promise(). Он будет иметь функцию с двумя аргументами, resolve и reject.

Мы позвоним resolve когда наша задача успешна, и reject когда задача оказывается неудачной. Мы будем использовать терминологию лотереи, которую я использовал для объяснения концепции обещаний в вышеприведенном разделе.

Скажем, если Math.random() дает значение ниже или равное 0,5, мы выигрываем в лотерею. Иначе мы проигрываем в лотерею. Если условие не соответствует действительности, код выдает новую ошибку для лучшего понимания ошибки в консоли. Поэтому мы можем предоставлять пользователю собственные ошибки для лучшего понимания.

В примере выше, если Math.rondom() менее 0,5, это означает, что пользователь проиграл лотерею. Так что мы выбрасываем нашу собственную ошибку Better luck next time чтобы пользователь понял, что он проиграл лотерею.

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

let lottery = new Promise(function(resolve, reject){
    console.log("Lottery is happening");

    setTimeout(() => {    
        if(Math.random() >= 0.5){
            resolve("You Won!!!")
        }
        else{
            reject(new Error("Better luck next time"))
        }   
    }, 5000);

})

lottery.then((response) =>{
    console.log(response);
}).catch((err) =>{
    console.log(err);
})

Мы используем обещание с помощью then() метод. Он напечатает ответ, который мы предоставили resolve(). Если обещание отклонено, мы обнаружим ошибку в catch() метод. Ошибка возникнет из reject() аргумент, который мы упомянули в собственном обещании.

Как использовать Promises с помощью Async/await

Потребление обещаний с помощью метода then() иногда может оказаться беспорядочным. Итак, у нас есть альтернативный метод потребления промисов под названием async/await.

Просто имейте в виду, что async/await будет использоваться then() метод потребления обещаний за кулисами.

Зачем использовать async/await, если у нас есть then() метод? Мы используем async/await, потому что им легко воспользоваться. Если мы начнем связывать методы с промесями с помощью then() цепочка будет очень длинной и сложной с добавлением нескольких методов then(). Следовательно, async/await проще.

Вот как работает async/await:

const fetchAPI = async function(){
    const res = await fetch('
    const data = await res.json()
    console.log(data);
}
fetchAPI()
console.log("FIRST");
123123

В вышеприведенном коде мы сначала вызываем fetchAPI(), чтобы увидеть асинхронное поведение функции. Затем он регистрирует «ПЕРВЫЙ». Согласно асинхронному JavaScript, fetchAPI() должен работать в фоновом режиме и не блокировать выполнение кода. В результате FIRST регистрируется, а затем отображается результат fetchAPI.

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

Теперь вы можете подумать, как обрабатывать ошибки? Для этого мы можем использовать try…catch() для обработки ошибок в async/await.

Мы можем использовать try...catch() также в ванильном JavaScript. Но это также может помочь нам обрабатывать ошибки в асинхронном JavaScript с помощью async/await.

try...catch() похож на catch() метод в then() используя catch() метод цепочки. Здесь мы попробуем код в try блокировать. Если это работает удачно, то трудности нет.

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

Давайте посмотрим, как это работает с примером кода, который мы использовали.

const fetchAPI = async function(){
    try{
        const res = await fetch('
        if(!res.ok){
            throw new Error("Custom Error")
        }
        const data = await res.json()
        console.log(data);
    } catch(err){
        console.log(err);
    }
}


fetchAPI()
console.log("FIRST");

Сначала мы заворачиваем асинхронный код в a try блокировать. Тогда в catch блок, мы регистрируем ошибку. В блоке попытки если res.ok false, мы создаем нашу собственную ошибку с помощью throw new Error который catch получите. Затем мы входим в консоль.

Пока мы узнали об асинхронном коде, then() и catch() методы и обработка асинхронного кода с помощью async/await. Но что если мы хотим вернуть значение из асинхронной функции с помощью async/await?

Когда вы работаете с асинхронным кодом, часто необходимо возвращать значение из async чтобы другие части программы могли использовать результат асинхронной операции.

Например, если вы делаете HTTP-запрос для получения данных с API, вы захотите вернуть данные ответа функции вызова, чтобы их можно было обработать или отобразить пользователю.

Ну мы можем это сделать. Посмотрите на пример ниже:

const fetchAPI = async function(){
    try{
        const res = await fetch('
        if(!res.ok){
            throw new Error("Custom Error")
        }
        const data = await res.json()
        console.log(data);
        return "Done with fetchAPI"
    } catch(err){
        console.log(err);
        throw new Error("Custom Error")
    }
}


console.log(fetchAPI())

Если мы зарегистрируем fetchAPI, мы получим выполненное обещание. Вы очень хорошо знаете, как обращаться с этими обещаниями. Мы будем делать это с помощью then() метод.

const fetchAPI = async function(){
    try{
        const res = await fetch('
        if(!res.ok){
            throw new Error("Custom Error")
        }
        const data = await res.json()
        console.log(data);
        return "Done with fetchAPI"
    } catch(err){
        console.log(err);
        throw new Error("Custom Error")
    }
}


fetchAPI().then((msg) =>{
    console.log(msg);
}).catch((err) =>{
    console.log(err);
})

Теперь, когда мы запускаем нашу программу, мы увидим наше возвращенное сообщение от try заблокировать с помощью async/await, войдя в консоль.

Но что если была ошибка в async/await? FetchAPI с методом then() будет все равно регистрировать его, и он будет неопределенным.

Чтобы избежать этого в блоке catch, мы выбрасываем новую ошибку и используем метод catch() для обнаружения этой ошибки после метода then().

Попытайтесь изменить then() и catch() методы с async/await. Это будет хорошим упражнением, чтобы понять, что вы узнали в этой статье.

В JavaScript есть два распространенных способа работы с асинхронными операциями: then/catch цепочка методов и async/await. Оба метода можно использовать для обработки промесей, являющихся объектами, представляющими окончательное завершение (или неудачу) асинхронной операции.

then/catch цепочка методов является более традиционным способом обработки асинхронных операций, хотя async/await это более новый синтаксис, который предлагает более короткую и более легкую для чтения альтернативу.

Как запускать Promises параллельно

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

const fetchAPI = async function(country1,country2,country3){
    try{
        const res1 = await fetch(`
        const res2 = await fetch(`
        const res3 = await fetch(`
        
        
        const data1 = await res1.json()
        const data2 = await res2.json()
        const data3 = await res3.json()
        console.log(data1[0].capital[0]);
        console.log(data2[0].capital[0]);
        console.log(data3[0].capital[0]);
        return "Done with fetchAPI"
    } catch(err){
        console.log(err);
        throw new Error("Custom Error")
    }
}


fetchAPI("canada", "germany", "russia")

В приведенном выше коде мы совершаем три вызова fetch, затем превращаем их в json() и регистрируем их капиталы.

Но если вы нажмете «Проверить» и увидите на вкладке сети res2 ждет завершения res1, а res3 ждет завершения res2.

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

321

Чтобы преодолеть эту проблему производительности, мы можем использовать что-нибудь под названием Promise.all . Он вызовет три запроса на выборку одновременно, что, в свою очередь, снизит время получения и улучшит производительность нашей программы.

Как использовать Promise.all()

С помощью Promise.all() мы можем выполнять несколько промесей параллельно, что повысит производительность. Promise.all() принимает массив как аргумент, являющийся промесями, и выполняет их параллельно.

let promise1 = new Promise((resolve) =>{
    setTimeout(() =>{
       resolve("First Promise")
    }, 2000)
})

let promise2 = Promise.resolve("Second Promise")

let returnedPromises = Promise.all([promise1,promise2]).then((res) =>{
    console.log(res);
})

Результатом использования promise.all() является то, что оба обещания выполнялись параллельно.

2121

Я надеюсь, что после прочтения этого руководства вы лучше поймете асинхронный JavaScript. Не стесняйтесь обращаться ко мне для обсуждения и предложения.

Вы можете следить за мной на:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *