Функция раннего обратного вызова
Мы часто пишем функции обратного вызова, такие как:
ajax(url, (res) => {
console.log(res);
})
Но у этой функции обратного вызова есть большой недостаток, то есть она будет писатьад обратного звонка(Callback hell
).
Например, если несколько обратных вызовов имеют зависимости, это можно записать так:
ajax(url, (res) => {
console.log(res);
// ...处理代码
ajax(url2, (res2) => {
console.log(res2);
// ...处理代码
ajax(url3, (res3) => {
console.log(res3);
// ...处理代码
})
})
})
Этоад обратного звонка:
- Во встроенной функции есть связь, и она влияет на все тело, изменение одной повлияет на другие места.
- Есть много встроенных функций, что делать, если возникает ошибка? это загадка
Преимущества и недостатки ранних функций обратного вызова:
- Преимущество: решеносинхронная блокировка(Пока есть задача, которая занимает много времени, следующие задачи должны стоять в очереди, что задержит выполнение всей программы)
- недостаток:ад обратного звонка;Не работает
try catch
поймать ошибки; не можетreturn
план переходаGenerator
Недавно представлен ES6Generator
функция (функция генератора), доступ к которой можно получить черезyield
Ключевое слово приостанавливает поток выполнения функции, позволяя изменить поток выполнения, тем самым предоставляя решение для асинхронного программирования. Самая большая особенностьМожет контролировать выполнение функций.
Generator
Есть две части, которые отличаются от обычных функций:
- один в
function
После этого перед именем функции стоит*
, который используется для представления функции какGenerator
функция - Внутри функции есть
yield
Выражение, используемое для определения состояния внутри функции
Generator
Конкретное использование функции выглядит следующим образом:
- существует
Generator
Выполните код внутри функции, если вы столкнулисьyield
ключевое слово, то движок JS вернет содержимое после ключевого слова наружу и приостановит выполнение функции. - Внешние функции могут быть
next
Метод возобновляет выполнение функции.
function* fn() {
console.log("one");
yield '1';
console.log("two");
yield '2';
console.log("three");
return '3';
}
передачаGenerator
Функция аналогична вызову обычной функции, добавьте имя функции после функции.()
Да, ноGenerator
Функция не выполняется немедленно, как обычная функция, вместо этогоВозвращает указатель на объект внутреннего состояния, поэтому вызовите объект итератораIterator
изnext
метод,Указатель начнет выполняться с начала функции или с того места, где он остановился..
следующим образом:
next
метод:
В основном,next
Когда метод не передает параметры,yield
Возвращаемое значение выраженияundefined
. когдаnext
Когда параметр передается, параметр будет использоваться как предыдущий шагyield
Возвращаемое значение.
Generator
Генератор тоже пишет асинхронный код синхронным способом, и он тоже может решить проблему callback hell, но это сложно понять.Надеюсь следующий пример поможет вам разобратьсяGenerator
Строитель:
function* sum(a) {
console.log('a:', a);
let b = yield 1;
console.log('b:', b);
let c = yield 2;
console.log('c:', c);
let sum = a + b + c;
console.log('sum:', sum)
return sum;
}
-
next
Когда никакие параметры не передаются,yield
вернутьundefined
Как показано ниже:
-
при первом исполнении
next
, аргументы игнорируются, и функция останавливается наyield 1
, так что возвращайся1
-
при выполнении во второй раз
next
Если параметр не передается, тоyield 1
то, что возвращаетсяundefined
,такb
Значениеundefined
-
В третий раз то же самое,
c
ценностьundefined
-
когда
next
Когда параметр передается, параметр будет использоваться как предыдущий шагyield
Возвращаемое значение
Как показано ниже:
- при первом исполнении
next
, передать параметр (20
) игнорируется, и функция приостанавливается вyield 1
, так что возвращайся1
- при выполнении во второй раз
next
, передать параметр30
, так какyield 1
возвращаемое значение, поэтомуb = yield 1
,b
Значение30
- при выполнении во второй раз
next
, передать параметр40
, так какyield 2
возвращаемое значение, поэтомуc = yield 2
,c
Значение40
сопрограмма
мы знаем,async
/await
это автоматическийGenerator
функция, описанная вышеGenerator
Функция, то нужно ввестиКак двигатель V8 реализует приостановку и возобновление функциичто о?
Чтобы понять, почему функции могут быть приостановлены и возобновлены, сначала необходимо понятьсопрограммаКонцепция чего-либо. Все мы знаем процессы и потоки, так что же такое сопрограмма?
Корутины легче, чем потоки. Вы можете думать о сопрограммах как о задачах, выполняемых в потоках.В потоке может существовать несколько сопрограмм, но одновременно в потоке может выполняться только одна сопрограмма.Например, текущее выполнение — это сопрограмма A, чтобы запустить сопрограмму B, затем сопрограмма A должна передать управление основным потоком сопрограмме B, что отражается в том, что сопрограмма A приостанавливает выполнение, а сопрограмма B возобновляется. выполнение; Точно так же сопрограмма A также может быть запущена из сопрограммы B. как правило,Если сопрограмма B запускается из сопрограммы A, мы называем сопрограмму A родительской сопрограммой сопрограммы B..
Точно так же, как процесс может иметь несколько потоков, поток может иметь несколько сопрограмм. Самое главное, сопрограммы не управляются ядром операционной системы, а полностью управляютсяс программным управлением(то есть выполняется в пользовательском режиме). Преимущество этого заключается в том, что производительность была значительно улучшена, и он не будет потреблять ресурсы, такие как переключение потоков.
Можно комбинировать с кодом, чтобы понять:
function* genDemo() {
console.log("开始执行第一段")
yield 'generator 2'
console.log("开始执行第二段")
yield 'generator 2'
console.log("开始执行第三段")
yield 'generator 2'
console.log("执行结束")
return 'generator 2'
}
console.log('main 0')
let gen = genDemo()
console.log(gen.next().value)
console.log('main 1')
console.log(gen.next().value)
console.log('main 2')
console.log(gen.next().value)
console.log('main 3')
console.log(gen.next().value)
console.log('main 4')
Процесс выполнения показан на рисунке ниже, и вы можете сосредоточиться на переключении между сопрограммами:
На рисунке мы видим четыре правила сопрограммы:
- вызвав функцию генератора
genDemo
создатьсопрограммаgen
, после создания,gen
Сопрограмма не выполняется немедленно. - позволить
gen
выполнение сопрограммы,нужно позвонитьgen.next
. - Когда сопрограмма выполняется, вы можетепройти через
yield
Ключевое слово для паузыgen
выполнение сопрограмми вернуть основную информацию родительской сопрограмме. - Если сопрограмма встречает
return
ключевое слово, то движок JS завершит текущую сопрограмму иreturn
Следующее содержимое возвращается в родительскую сопрограмму.
Переключение между Coroutines:
-
gen
Сопрограмма и родительская сопрограмма выполняются интерактивно в основном потоке, а не одновременно.пройти черезyield
а такжеgen.next
завершитьиз. - когда в
gen
вызывается в сопрограммеyield
метод, движок JS сохранитgen
Информация о текущем стеке вызовов сопрограммы и восстановление информации о стеке вызовов родительской сопрограммы. Аналогично, при выполнении в родительской сопрограммеgen.next
, JS-движок сохранит информацию о стеке вызовов родительской сопрограммы и восстановит ее.gen
Информация о стеке вызовов сопрограммы.
На самом деле в JSGenerator
Генераторы — это реализация сопрограмм.
зрелое решениеPromise
оPromise
, вы можете перейти к моей последней статье:«Обещание асинхронного программирования: от использования до рукописной реализации (4200 слов)», подробно описано в этой статьеPromise
Как решить проблему callback hell, разбираемсяPromise
И происхождение микрозадач, а затем пошаговая деконструкция почерка для достижения простойPromise
, и, наконец, кратко представил и реализовал некоторые вручнуюPromise
API, в том числеPromise.all
,Promise.allSettled
,Promise.race
,Promise.finally
и т.д. API.
окончательное решениеasync
/await
использоватьPromise
Отличное решение для ада обратного вызова, но этот путь полонPromise
изthen()
метод, если поток обработки более сложный, то весь код будет заполненthen
, семантика неочевидна, и код не может хорошо представить поток выполнения.
По этой причине ES7 представилasync
/await
, значительное улучшение по сравнению с асинхронным программированием в JavaScript, обеспечивающееВозможность использовать синхронный код для асинхронного доступа к ресурсам без блокировки основного потока.и сделать логику кода более понятной.
фактическиasync
/await
Секрет технологии заключается в том,Promise
а такжеGenerator
Приложение генератора на низком уровнеМикрозадачи и приложения сопрограмм. выяснитьasync
а такжеawait
работает, мы получили это правильноasync
а такжеawait
Отдельный анализ.
async
async
что именно? Согласно определению MDN,async
пропускВыполнять асинхронно и возвращать неявноPromise
функционировать в результате. Сосредоточьтесь на двух словах: асинхронное выполнение и неявный возврат.Promise
.
Давайте посмотрим, как вернуть неявноPromise
, обратитесь к следующему коду:
async function async1() {
return '秀儿';
}
console.log(async1()); // Promise {<fulfilled>: "秀儿"}
Выполните этот код, вы можете увидеть вызовasync
объявленasync1
функция возвращаетPromise
объект, состояниеresolved
, возвращенный результат выглядит следующим образом:Promise {<fulfilled>: "秀儿"}
. а такжеPromise
связанные звонкиthen
Возвращаемое значение обрабатывается таким же образом.
await
await
нужно следоватьasync
Используйте его в сочетании со следующим кодом, чтобы увидетьawait
что именно это:
async function foo() {
console.log(1)
let a = await 100
console.log(a)
console.log(2)
}
console.log(0)
foo()
console.log(3)
стоятьсопрограммаДавайте посмотрим на общую блок-схему выполнения этого кода с точки зрения:
В сочетании с приведенным выше рисунком для анализаasync
/await
Процесс выполнения:
- Сначала выполните
console.log(0)
Это заявление, распечатайте0
. - с последующей казнью
foo
функцию, потому чтоfoo
функцияasync
отмечено, поэтому при входе в функцию JS-движок сохранит текущий стек вызовов и другую информацию, а затем выполнитfoo
в функцииconsole.log(1)
заявление и распечатать1
. - Быть казненным
await 100
, это создаст значение по умолчаниюPromise
объект- Код выглядит следующим образом:
let promise_ = new Promise((resolve,reject){ resolve(100) })
- в этот
promise_
В процессе создания объекта вы можете увидеть вexecutor
функция называетсяresolve
функция, JS-движок отправит задачу в очередь микрозадач. - Затем JS-движок приостановит выполнение текущей сопрограммы, передаст управление основным потоком родительской сопрограмме для выполнения и
promise_
Объект возвращается в родительскую сопрограмму. - Управление основным потоком было передано родительской сопрограмме.На данный момент единственное, что должна сделать родительская сопрограмма, это вызвать
promise_.then
контролироватьpromise
изменение состояния.
- Код выглядит следующим образом:
- Далее продолжаем выполнять процесс родительской сопрограммы, выполняем
console.log(3)
, и распечатайте3
. - Затем родительская сопрограмма завершится, перед окончанием она войдет в контрольную точку микрозадачи, а затем выполнит очередь микрозадачи.
resolve(100)
Задача ожидает выполнения. Когда она будет выполнена здесь, она сработаетpromise_.then
Функция обратного вызова в , как показано ниже:
promise_.then((value) => {
// 回调函数被激活后
// 将主线程控制权交给foo协程,并将vaule值传给协程
})
- После того, как функция обратного вызова будет активирована, она передаст управление основным потоком
foo
функциональные сопрограммы, и в то же времяvalue
значение передается сопрограмме. -
foo
После активации COROUTINE, предыдущийvalue
значение, присвоенное переменнойa
,Потомfoo
Сопрограмма продолжает выполнять последующие операторы и возвращает управление родительской сопрограмме после завершения выполнения.
Вышеупомянутоеawait
/async
процесс исполнения. просто такasync
а такжеawait
За кулисами выполняется много работы, поэтому мы можем писать асинхронный код синхронным способом.
Конечно, есть и недостатки, потому чтоawait
Преобразование асинхронного кода в синхронный код, если несколько асинхронных кодов не имеют зависимостей, но используютсяawait
приведет к снижению производительности.
async
/await
Суммировать
-
Promise
Модель программирования по-прежнему наполнена большим количествомthen
метод хоть и решает проблему callback hell, но все же имеет недочеты в семантике, а в коде полно многоthen
функция, этоasync
/await
Причина появления. - использовать
async
/await
Можно написать асинхронный код в стиле синхронного кода, потому чтоasync
/await
Используемая базовая технологияGenerator
генератор иPromise
,Generator
Генераторы — это реализации сопрограмм, которые используютGenerator
Генераторы можно использовать для приостановки и возобновления функций генератора. - Кроме того, двигатель V8 также
async
/await
Выполняйте переносы на уровне синтаксиса, поэтому понимание лежащего в их основе кода поможет углубить ваше пониманиеasync
/await
понимание. -
async
/await
Это, несомненно, очень большая инновация в области асинхронного программирования, а также основной стиль программирования в будущем. Фактически, помимо JavaScript, также были представлены такие языки, как Python, Dart и C#.async
/await
, его использование не только делает код чище и красивее, но и гарантирует, что функция всегда возвращаетPromise
.
Сводка по асинхронному программированию
- Хотя ранняя асинхронная функция обратного вызова решала проблему синхронной блокировки, было легко написать ад обратного вызова.
-
Generator
Самая большая особенность генераторов заключается в том, что они могут контролировать выполнение функций и являются реализацией сопрограмм. -
Promise
Для получения дополнительной информации см. мою статью:«Обещание асинхронного программирования: от использования до рукописной реализации (4200 слов)» -
async
/await
Его можно рассматривать как окончательное решение для асинхронного программирования.Он пишет асинхронный код синхронным способом, который может поставитьawait
Это расценивается как знак отказаться от нити и выполняется в первую очередь.async
Код вне функции, дождитесь пустым стеком вызова, а затем перезвонитьawait
код позади.