Асинхронное программирование JavaScript

внешний интерфейс программист JavaScript Promise

предисловие

Мы слышали цитату с самого начала, когда впервые изучили JavaScript:JS является однопоточным, асинхронным по своей сути, подходит для операций с интенсивным вводом-выводом, а не с интенсивным использованием процессора.. Однако большинство разработчиков JavaScript никогда серьезно не задумывались о том, как и почему в их программах возникает асинхронность, и не изучали другие способы борьбы с асинхронностью. До сих пор есть много людей, которые настаивают на том, что функции обратного вызова вполне достаточно.

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

Эта статья также будет синхронизирована с моейперсональный сайт.

текст

Шаг 1 — функция обратного вызова

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

setTimeout(function () {
    console.log('Time out');
}, 1000);

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

Рассмотрим такое требование:

http-1

Выше приведена диаграмма последовательности входа в апплет WeChat. Наши требования аналогичны ей, но есть некоторые отличия. Чтобы получить часть бизнес-данных, весь процесс делится на 3 этапа:

  1. Вызовите интерфейс секретного ключа, чтобы получить ключ
  2. Вызовите интерфейс входа с помощью ключа, чтобы получить токен и идентификатор пользователя.
  3. Вызовите бизнес-интерфейс с токеном и идентификатором пользователя, чтобы получить данные

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

Мы пишем кусок кода для достижения вышеуказанных требований:

let key, token, userId;

$.ajax({
    type: 'get',
    url: 'http://localhost:3000/apiKey',
    success: function (data) {
        key = data;
        
        $.ajax({
            type: 'get',
            url: 'http://localhost:3000/getToken',
            data: {
                key: key
            },
            success: function (data) {
                token = data.token;
                userId = data.userId;
                
                $.ajax({
                    type: 'get',
                    url: 'http://localhost:3000/getData',
                    data: {
                        token: token,
                        userId: userId
                    },
                    success: function (data) {
                        console.log('业务数据:', data);
                    },
                    error: function (err) {
                        console.log(err);
                    }
                });
            },
            error: function (err) {
                console.log(err);
            }
        });
    },
    error: function (err) {
        console.log(err);
    }
});

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

Почему происходит это явление?

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

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

Здесь мы вводим функцию обратного вызова для решения асинхроннойВопрос 1: Ад обратного звонка.

Функция обратного вызова будет продолжать существовать другие вопросы? Давайте глубоко подумать о концепции обратного вызова:

// A
$.ajax({
    ...
    success: function (...) {
        // C
    }
});
// B

A и B происходят сейчас, под непосредственным контролем основной программы JavaScript, в то время как C откладывается до будущего и под контролем третьей стороны, в данном случае функции $.ajax(...). В принципе, такая передача управления обычно не вызывает особых проблем для программы.

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

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

Поскольку вашу функцию обратного вызова выполняет неконтролируемая третья сторона, могут возникнуть следующие проблемы, которых, конечно, обычно не бывает:

  1. Слишком ранний обратный вызов
  2. Слишком поздний обратный вызов
  3. Вызывается слишком много или слишком мало обратных вызовов
  4. Не удалось успешно передать необходимые параметры в функцию обратного вызова.
  5. проглотить возможные ошибки или исключения
  6. ......

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

Здесь мы вводим функцию обратного вызова для обработки асинхронногоВторой вопрос: инверсия управления.

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

1. Отсутствие последовательности: трудности отладки, вызванные адом обратных вызовов, несовместимы с тем, как думает мозг.
2. Отсутствие доверия: ряд проблем с доверием, вызванных инверсией контроля.

Итак, как решить эти две проблемы, первооткрыватели начали исследовать...

Step2 - Promise

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

А что такое Обещание, каждый должен кое-что знать, вотСпецификация PromiseA+, ES6 Promise или jQuery Promise, разные библиотеки имеют разные реализации, но все следуют одному и тому же набору спецификаций, поэтому Promise не относится к конкретной реализации,Это спецификация, набор механизмов для обработки асинхронности JavaScript..

Давайте рефакторим приведенный выше пример многоуровневого вложения обратного вызова с помощью Promise:


let getKeyPromise = function () {
    return new Promsie(function (resolve, reject) {
        $.ajax({
            type: 'get',
            url: 'http://localhost:3000/apiKey',
            success: function (data) {
               let key = data;
               resolve(key);         
            },
            error: function (err) {
                reject(err);
            }
        });
    });
};

let getTokenPromise = function (key) {
    return new Promsie(function (resolve, reject) {
        $.ajax({
            type: 'get',
            url: 'http://localhost:3000/getToken',
            data: {
                key: key
            },
            success: function (data) {
                resolve(data);         
            },
            error: function (err) {
                reject(err);
            }
        });
    });
};

let getDataPromise = function (data) {
    let token = data.token;
    let userId = data.userId;
    
    return new Promsie(function (resolve, reject) {
        $.ajax({
            type: 'get',
            url: 'http://localhost:3000/getData',
            data: {
                token: token,
                userId: userId
            },
            success: function (data) {
                resolve(data);         
            },
            error: function (err) {
                reject(err);
            }
        });
    });
};

getKeyPromise()
    .then(function (key) {
        return getTokenPromise(key);
    })
    .then(function (data) {
        return getDataPromise(data);
    })
    .then(function (data) {
        console.log('业务数据:', data);
    })
    .catch(function (err) {
        console.log(err);
    }); 

Видно, что Promise действительно в определенной степени улучшает способ написания callback-функций.Самый очевидный момент — убрано горизонтальное расширение.Независимо от того, сколько бизнес-зависимостей, данные получаются через множественные then(...) , так что Код расширяется только по вертикали; другой момент в том, что логика более очевидна, асинхронный бизнес извлекается в одну функцию, видно, что весь процесс выполняется шаг за шагом, уровень зависимости также очень ясно, и окончательные данные, необходимые в целом, последний шаг кода получен.

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

Что я хочу обсудить здесь, так это то,Как промисы решают проблему недоверия, вызванную инверсией управления.

Прежде всего, для ясности, Promise может гарантировать следующие ситуации, цитируемые изJavaScript | MDN:

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

Далее мы обсудим ряд проблем с доверием, вызванных асинхронной обработкой функций обратного вызова, упомянутых выше.Если Promise используется для решения этих проблем, конечно, предполагается, что реализованный Promise полностью соответствуетСпецификация PromiseA+.

звонить слишком рано

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

Однако, согласноСпецификация PromiseA+, Promises не нужно беспокоиться об этом, потому что даже Promise, который завершается немедленно (аналогично new Promise(function (resolve, reject) {resolve(2);})) нельзя наблюдать синхронно.

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

звоню слишком поздно

Когда Promise создает объект и вызывает resolve(...) или reject(...), функция обратного вызова, зарегистрированная этим Promise через then(...), будет запущена в следующий асинхронный момент времени.

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

Пример выглядит следующим образом:

p.then(function () {
    p.then(function () {
        console.log('C');
    });
    console.log('A');
})
.then(funtion () {
    console.log('B');
});

// 打印 A B C

Как вы можете видеть из этого примера, C не может прервать или вытеснить B, поэтому Promise не вызывается слишком поздно.Пока вы регистрируете then(...), он обязательно будет вызван по порядку, потому что именно так работает Promise. .

Обратный вызов не вызывается

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

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

p.then(function (data) {
    console.log(data);
    foo.bar();       // 这里没有定义foo,所以这里会报Type Error, foo is not defined
}, function (err) {

});

Слишком много или слишком мало звонков

согласно сСпецификация PromiseA+, правильное количество вызовов обратного вызова должно быть 1. «Слишком мало» означает отсутствие вызова, как объяснялось ранее.

«Слишком много» легко объяснить, и обещание обещания делает его только резолюцией один раз. Если вы по разным причинам, обещание создает попытку кода, чтобы вызвать несколько разрешений (...) или отклонить (...) или пытаться позвонить, то это обещание будет принимать только первое разрешение и тихо. звонки.

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

Не удалось передать значение параметра

Если вы не передаете никакого значения для разрешения (...) или отклонения (...), тогда значениеundefined. Но каким бы ни было это значение, оно будет передано всем функциям обратного вызова, зарегистрированным в then(...).

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

проглотить возможные ошибки или исключения

Если в какой-либо момент во время создания обещания или при просмотре результата его разрешения возникает исключение JavaScript, такое как TypeError или ReferenceError, исключение будет перехвачено, и обещание будет отклонено.

Пример выглядит следующим образом:

var p = new Promise(function (resolve, reject) {
    foo.bar();    // foo未定义
    resolve(2);
});

p.then(function (data) {
    console.log(data);    // 永远也不会到达这里
}, function (err) {
    console.log(err);    // err将会是一个TypeError异常对象来自foo.bar()这一行
});

Исключение JavaScript, возникшее в foo.bar(), привело к отклонению обещания, которое вы можете перехватить и ответить на него.

Не всем потомкам можно доверять

До сих пор мы обсуждали, как использование промисов может избежать многих из вышеупомянутых проблем с доверием, вызванных инверсией управления. Однако, как вы, должно быть, заметили, промисы не полностью избавляются от обратных вызовов, они просто меняются местами, где передаются обратные вызовы. Вместо того, чтобы передавать обратный вызов foo(...) для выполнения третьей стороной, мы получаем что-то (объект Promise) от foo(...) и передаем обратный вызов этой вещи.

Но почему это более надежно, чем просто использование обратных вызовов? Как мы можем быть уверены, что возвращенная вещь на самом деле является доверенным промисом?

В Promise уже есть решение этой проблемы, и решение для Promise, реализованное в ES6, таково:Promise.resolve(...).

Если вы передадите в Promise.resolve(...) непромисное, не подлежащее дальнейшему немедленное значение, вы получите промис, заполненный этим значением.

Пример выглядит следующим образом:

var p1 = new Promise(function (resolve, reject) {
    resolve(2);
});

var p2 = Promise.resolve(2);

// 这里p1和p2的效果是一样的

И если вы передадите настоящий промис в Promise.resolve(...), он просто вернет тот же промис.

var p1 = Promise.resolve(2);
var p2 = Promise.resolve(p1);

p1 === p2;    // true

Что еще более важно, если Promise.resolve(...) будет передано значение, отличное от Promise, первое попытается раскрутить значение, и процесс раскрутки будет продолжаться до тех пор, пока не будет извлечено конкретное конечное значение, не похожее на Promise.

Пример выглядит следующим образом:

var p = {
    then: function (cb, errCb) {
        cb(2);
        errCb('haha');
    }
};

// 这可以工作,因为函数是一等公民,可以当做参数进行传递
p.then(function (data) {
    console.log(data);    // 2
}, function (err) {
    console.log(err);    // haha
});

Этот p является условным, но не настоящим промисом, его поведение не совсем такое же, как у промиса, он вызывает как обратный вызов успеха, так и обратный вызов отклонения, он не заслуживает доверия.

Тем не менее, мы можем передать такой p в Promise.resolve(...) и получить ожидаемый нормализованный безопасный результат:

Promise.resolve(p)
    .then(function (data) {
        console.log(data);    // 2
    }, function (err) {
        console.log(err);    // 永远不会到达这里
    });

Как обсуждалось ранее, Promise принимает только одно разрешение, и если resolve(...) или reject(...) вызываются несколько раз, последнее будет автоматически проигнорировано.

Promise.resolve(...) может принять любое значение THENABLE, чтобы снять отметку с его неподдерживаемого значения. From Promise.resolve(...) — это настоящее обещание, доверенное значение. Если вам приходит уже настоящее обещание, то вы получаете его само, поэтому оно полностью лишено доверия через фильтрацию Promise.Resolve(...).

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

Шаг 3 — Генератор Генератор

На шаге 1 мы определили две ключевые проблемы, связанные с выражением асинхронного процесса с помощью обратного вызова:

  1. Асинхронность на основе обратного вызова не соответствует каноническому способу выполнения задач мозгом
  2. Обратные вызовы не заслуживают доверия из-за инверсии управления

На шаге 2 мы подробно рассказали, как Promise меняет контроль над обратным вызовом и восстанавливает надежность.

Теперь мы обратим наше внимание на последовательный, кажущийся синхронным стиль выражения асинхронного управления потоком, которыйГенераторы в ES6.

Азертивированный протокол и итераторский протокол

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

Итерируемый протокол

Итерируемый протоколЗапустите объекты JavaScript, чтобы определить или настроить их поведение при итерации, например (определить), какие значения можно зациклить (получить) в конструкции for...of. Следующие встроенные типы являются встроенными итерациями и имеют поведение итерации по умолчанию:

  1. Array
  2. Map
  3. Set
  4. String
  5. TypedArray
  6. Объект аргументов для функции
  7. Объект NodeList

Обратите внимание, что объект не соответствует итерируемому протоколу..

Чтобы быть итерируемым, объект должен реализовать метод @@iterator, что означает, что объект (или какой-либо объект в его цепочке прототипов) должен иметь свойство с именем Symbol.iterator:

Атрибуты стоимость
[Symbol.iterator] Функция без параметров, которая возвращает объект, возвращаемый объект соответствует протоколу итератора.

Когда объект необходимо повторить (например, начать в цикле for...of), его метод @@iterator вызывается без аргументов и возвращает итератор для получения значения в итерации.

протокол итератора

протокол итератораОпределяет стандартный способ создания конечной или бесконечной последовательности значений.
Когда объект считается итератором, он реализует метод next() и имеет следующие значения:

Атрибуты стоимость
next Функция без аргументов, которая возвращает объект. Возвращенный объект имеет два свойства:
1. сделано (логическое)
- true, если итератор прошел итерируемую последовательность. В этом случае value может описывать возвращаемое значение итератора.
- false, если итератор может выдать следующее значение в последовательности. Это равносильно тому, что не указано ни то, ни другое вместе с атрибутом done.
2. value- Любое значение JavaScript, возвращаемое итератором. Может быть проигнорировано, когда done равно true.

Пример использования итерируемого протокола и протокола итератора:

var str = 'hello';

// 可迭代协议使用for...of访问
typeof str[Symbol.iterator];    // 'function'

for (var s of str) {
    console.log(s);    // 分别打印 'h'、'e'、'l'、'l'、'o'
}

// 迭代器协议next方法
var iterator = str[Symbol.iterator]();

iterator.next();    // {value: "h", done: false}
iterator.next();    // {value: "e", done: false}
iterator.next();    // {value: "l", done: false}
iterator.next();    // {value: "l", done: false}
iterator.next();    // {value: "o", done: false}
iterator.next();    // {value: undefined, done: true}

Мы сами реализуем объект, чтобы он соответствовалИтерируемый протокола такжепротокол итератора:

var something = (function () {
    var nextVal;
    
    return {
        // 可迭代协议,供for...of消费
        [Symbol.iterator]: function () {
            return this;
        },
        
        // 迭代器协议,实现next()方法
        next: function () {
            if (nextVal === undefined) {
                nextVal = 1;
            } else {
                nextVal = (3 * nextVal) + 6;
            }
            
            return {value: nextVal, done: false};
        }
    };
})();

something.next().value;    // 1
something.next().value;    // 9
something.next().value;    // 33
something.next().value;    // 105

Асинхронный с генератором

Что, если мы перепишем приведенный выше пример вложения обратных вызовов с помощью Generator? См. код:

function getKey () {
    $.ajax({
        type: 'get',
        url: 'http://localhost:3000/apiKey',
        success: function (data) {
            key = data;
            it.next(key);
        }
        error: function (err) {
            console.log(err);
        }
    });
}

function getToken (key) {
    $.ajax({
        type: 'get',
        url: 'http://localhost:3000/getToken',
        data: {
            key: key
        },
        success: function (data) {
            loginData = data;
            it.next(loginData);
        }
        error: function (err) {
            console.log(err);
        }
    });
}

function getData (loginData) {
    $.ajax({
        type: 'get',
        url: 'http://localhost:3000/getData',
        data: {
            token: loginData.token,
            userId: loginData.userId
        },
        success: function (busiData) {
            it.next(busiData);
        }
        error: function (err) {
            console.log(err);
        }
    });
}



function *main () {
    let key = yield getKey();
    let LoginData = yield getToken(key);
    let busiData = yield getData(loginData);
    console.log('业务数据:', busiData);
}

// 生成迭代器实例
var it = main();

// 运行第一步
it.next();
console.log('不影响主线程执行');

Обратите внимание на код внутри генератора *main(). Если не смотреть на ключевое слово yield, это синхронная форма записи, которая полностью соответствует привычкам мышления мозга. Асинхронный процесс инкапсулирован снаружи, и it.next() вызывается в функции успешного обратного вызова. , помещает возвращенные данные в очередь задач для постановки в очередь, а когда основной поток JavaScript простаивает, он по очереди вынимает задачи обратного вызова из очереди задач для выполнения .

Если мы продолжим занимать основной поток JavaScript, у нас не будет времени на выполнение задач в очереди задач:

// 运行第一步
it.next();

// 持续占用JavaScript主线程
while(1) {};    // 这里是拿不到异步数据的,因为没有机会去任务队列里取任务执行

Таким образом, генератор Generator решает проблему обработки асинхронного процесса функцией обратного вызова.Первая проблема: способ мышления, не соответствующий последовательному, линейному мышлению мозга..

Step4 - Async/Await

Мы представили Promise и Generator выше, а их объединение — это Async/Await.

Недостаток генератора заключается в том, что нам также необходимо вручную контролировать выполнение next().При использовании Async/Await, если за ожиданием следует обещание, он будет автоматически ожидать возвращаемого значения после разрешения обещания, разрешить( ...) или отклонить(.. .)Все будет в порядке.

Мы переписываем оригинальный пример в Async / ждут:

let getKeyPromise = function () {
    return new Promsie(function (resolve, reject) {
        $.ajax({
            type: 'get',
            url: 'http://localhost:3000/apiKey',
            success: function (data) {
               let key = data;
               resolve(key);         
            },
            error: function (err) {
                reject(err);
            }
        });
    });
};

let getTokenPromise = function (key) {
    return new Promsie(function (resolve, reject) {
        $.ajax({
            type: 'get',
            url: 'http://localhost:3000/getToken',
            data: {
                key: key
            },
            success: function (data) {
                resolve(data);         
            },
            error: function (err) {
                reject(err);
            }
        });
    });
};

let getDataPromise = function (data) {
    let token = data.token;
    let userId = data.userId;
    
    return new Promsie(function (resolve, reject) {
        $.ajax({
            type: 'get',
            url: 'http://localhost:3000/getData',
            data: {
                token: token,
                userId: userId
            },
            success: function (data) {
                resolve(data);         
            },
            error: function (err) {
                reject(err);
            }
        });
    });
};

async function main () {
    let key = await getKeyPromise();
    let loginData = await getTokenPromise(key);
    let busiData = await getDataPromise(loginData);
    
    console.log('业务数据:', busiData);
}

main();

console.log('不影响主线程执行');

Это можно увидеть с помощью Async/Await, полностью синхронизированной нотации, логические зависимости и зависимости данных очень четкие, просто нужно что-то с асинхронным пакетом Promise out, затем вызовы await на него, не нужны те же потребности, что и Генератору ручного управления next() исполнение.

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

Суммировать

В этой статье описывается разработка асинхронного программирования JavaScript в четыре этапа:

  1. Первый этап - callback-функция, но вызывает две проблемы:

    • Отсутствие последовательности: отладки трудностей, вызванные обратным списком ад, несовместимым с тем, как мозг думает
    • Отсутствие доверия: ряд проблем с доверием, вызванных инверсией контроля.
  2. Второй этап – обещания, Promise — это реализация, основанная на спецификации PromiseA+, которая решает проблему доверия, вызванную инверсией управления, и возвращает инициативу выполнения кода.
  3. Третий этап - генераторная функция Generator, Использование генератора позволяет нам писать код синхронно, что решает проблему последовательности, но нам нужно вручную управлять next(...) и успешно отправлять данные, возвращенные обратным вызовом, обратно в основной процесс JavaScript.
  4. Четвертый этап — асинхронный/ожиданиеAsync / await сочетает в себе обещание и генератор, а затем ждут ждут, с обещанием он автоматически ждет, пока ценность обещания обещания решает генератор для управления проблемой вручную, проблема выполнена, действительно реализована.Напишите асинхронный код синхронным способом.

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

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

Ссылаться на:

  1. developer.mozilla.org...