JavaScript: обещание, асинхронность и ожидание

Обещать

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

JavaScript предоставляет нам несколько способов вызова асинхронных операций. Первый способ — использовать объект Promise. Конструктор Promise принимает функцию обратного вызова в качестве аргумента, а сама функция обратного вызова принимает два аргумента, где первый аргумент обычно называется разрешением, а второй аргумент называется отклонением. Вот пример:

const createPromise = () => {
    return new Promise( (resolve, reject) => {
        setTimeout(() => {
            if (condition) {
                resolve('data');
            } else {
                reject('error');
            }
        }, 3000);
    });
};

В приведенном выше примере обратите внимание, что когда условие истинно, мы вызываем разрешение и передаем ему строку. Если условие ложно, мы вызываем отказ и передаем ему сообщение об ошибке. setTimeout заставляет эту операцию завершиться через 3 секунды. Причина, по которой мы используем resolve и reject, заключается в том, что мы можем связать обещание с предложениями then и catch для вызова соответствующего обратного вызова для обработки различных ситуаций. Например:

createPromise()
.then((data) => {
    console.log(data); // 'the data'
}).catch((error) => {
    console.log(error); // 'An error occurs'
})
console.log('the last line of code');

В приведенном выше примере мы присоединяем предложение then и предложение catch к функции createPromise(). Когда операция обратного вызова Promise завершается успешно (разрешается), будет вызываться обратный вызов в предложении then для обработки возвращаемых данных. В противном случае, если операция не удалась (отклонить), обратный вызов в предложении error будет вызван с сообщением об ошибке, указанным в качестве параметра обратного вызова.

Обратите внимание, что после вызова функции createPromise() у нас есть еще одна строка кода для вывода журнала консоли. Это может вызвать вопрос: будет ли последняя строка кода выполнена через три секунды? Ответ отрицательный, последняя строка кода будет выполнена сразу после createPromise(). Это преимущество использования Promise с предложениями then и catch, так как нам не нужно сидеть и ничего не делать в течение трех секунд, чтобы Promise вернул некоторые значения. JavaScript будет продолжать выполнять код после вызова createPromise() и вернуться к предложению then или catch после того, как некоторые значения будут возвращены из обещания, либо разрешить, либо отклонить. Функции обратного вызова в предложениях then и catch вызываются асинхронно, что позволяет нам загружать JavaScript с меньшим временем простоя.

Интересно отметить, что код обратного вызова Promise вызывается синхронно. Если мы поместим console.log внутри обратного вызова Promise, он будет вызываться перед последней строкой кода. Это имеет смысл, потому что нам нужно синхронно вызывать асинхронные функции внутри обратного вызова Promise. В противном случае у нас никогда не будет возможности сделать это, поскольку в нашем синхронном коде по умолчанию никогда не вызывается асинхронная функция.

Асинхронно и ждать

JavaScripts предоставляет нам другие способы обработки асинхронных операций с Promise. Ключевое слово async может быть помещено перед функцией, что делает функцию промисом. Следовательно, асинхронная функция может быть связана с предложениями then и catch. Вот пример объявления асинхронной функции:

const asyncFunc = async () => {
    console.log('step 1');
    console.log('step 2');
    try {
        await createPromise();
    } catch(error) {
        return 'error';
    }
    console.log('step 3');
    console.log('step 4');
    return 'return from asyncFunc'
};

Обратите внимание, что мы снова используем функцию createPromise() из предыдущего раздела, которая вызывает setTimeout для разрешения промиса через три секунды. Назовем эту асинхронную функцию:

asyncFunc().then((data) => {
    console.log(data);
});
console.log('the last line of the code');

Порядок вывода будет таким:

step 1
step 2
the last line of the code
step 3
step 4
return from asyncFunc

Обратите внимание, что мы используем ключевое слово await перед строкой, в которой мы вызываем createPromise(). Это должно сообщить JavaScript, что если createPromise() вызывает асинхронную операцию, выйти из асинхронной функции и выполнить следующий синхронный код и вернуться к асинхронной функции после завершения ожидаемой функции. Значение, возвращаемое асинхронной функцией, может быть обработано в предложении then.

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

Блок try и catch в асинхронной функции не нужен. Это просто способ обработки ожидаемой функции, поскольку ожидаемая функция является асинхронной, и ее можно разрешить или отклонить. Если ожидаемая функция отклонена, для обработки ошибки будет вызван код в предложении catch.

Таким образом, если мы хотим выйти из функции для асинхронной операции, мы объявляем функцию как асинхронную и используем ключевое слово await для аннотации функции, которая вызывает асинхронную операцию.

— — —

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