Асинхронный код, как он работает? Обещания и обратные вызовы
Я попытался найти ответ в Интернете, и сделал это частично. Но я до сих пор не могу полностью понять, как JS может запускать асинхронный код?
Мое видение вещей:
JS дают нам возможность асинхронного программирования. Это означает, что мы можем запустить первое задание, затем, когда оно выполняется, мы можем запустить второе задание и т. Д. Прежде чем js сможет запустить второе задание, оно должно быть освобождено от предыдущего задания. Это может быть достигнуто двумя способами:
- js сама по себе задача (код, который должен обрабатываться только js)
- js может запустить задачу, которая должна обрабатываться, например, файловой системой. В этом случае js выполняет свою работу, передает задачу в файловую систему и начинает обрабатывать другие задачи в очереди. Когда js освобожден и файловая система вернула результат, js может продолжить эту задачу.
Поэтому мы не можем достичь асинхронности, просто написав следующий код:
function doSth( callback ) {
let arr = [];
for ( let i=1e6; i > 0; i-- )
arr.push( i );
callback();
}
doSth( console.log.bind( null, 'I am callback' );
console.log( 'just a line' );
из-за того, что doSth() содержит только работу js, сначала она будет выполнена, а затем просто появится "просто строка"? Так что это не асинхронно, верно? Но если у нас будет заданная в цикле задача файловой системы, у нас будет асинхронная функция?
И еще один вопрос: действительно ли обещания являются асинхронными? Как они могут быть асинхронными (я имею в виду, могут ли другие задачи обрабатываться во время обработки обещаний), или обещания просто имитируют асинхронный код? * Я знаю, есть дополнительная очередь для обещаний.
Мэйби, я просто не понимаю какую-то базу? Я буду рад, если вы сможете объяснить мне, чтобы мои вопросы были более понятными для меня.
2 ответа
Я думаю, что вы правильно поняли.
Функция doSth
является синхронным и будет блокировать основной поток до его завершения. Вы предоставляете только API обратного вызова, но это не означает, что он волшебным образом станет асинхронным.
По правде говоря, весь код JS, который вы пишете, является синхронным, если вы не используете основные функции JS, которые определены как асинхронные (например, fs.readFile
, setTimeout
, $.ajax({...}).done
). Без них вы не сможете создать асинхронное поведение, вам придется писать свое собственное ядро JS с нуля, с циклом событий (на самом деле, я призываю вас зайти в Google и изучить, что javascript event loop
Я верю, что это прояснит для вас многое и даст вам гораздо лучшее представление о том, что происходит в ядре). Все сторонние библиотеки достигают асинхронного поведения просто потому, что они используют эти основные функции и обертывают их собственным кодом, предоставляя более элегантные и высокоуровневые API.
То же самое относится и к обещаниям. Да, они асинхронные, НО ТОЛЬКО если вы заполняете их асинхронным кодом. Действительно, они имеют некоторые дополнительные издержки и не запускают код сразу, но если одно обещание содержит только синхронный код, то его возможное выполнение будет блокировать основной поток до его завершения.
Посмотрите этот плейлист для легкого обзора асинхронного Javascript.
Но если вы действительно хотите понять все детали, прочитайте эту книгу.
Чтобы ответить на один из ваших основных вопросов, асинхронность не имеет ничего общего с файловой системой. Это все еще Javascript. Чтобы процитировать книгу, которую я рекомендовал:
Асинхронность - это "когда часть вашей программы запускается сейчас, а другая часть программы запускается позже - между настоящим моментом и позже возникает разрыв, когда ваша программа не выполняется активно".
Рассмотрим ваш код:
function doSth(callback) {
let arr = [];
for (let i=1e6; i > 0; i--){
arr.push(i);
}
callback();
}
doSth(console.log.bind(null, 'I am callback'));
console.log('just a line');
Это будет выводить
Я перезвонил
затем
просто линия
Однако, если вы изменили свой код на это:
function doSth(callback) {
let arr = [];
for (let i=1e6; i > 0; i--){
arr.push(i);
}
setTimeout(callback, 0);
}
doSth(console.log.bind(null, 'I am callback'));
console.log('just a line');
Ты получишь
просто линия
затем
Я перезвонил
По сути, это связано с тем, какая функция вызывает ваш обратный вызов. doSth не является асинхронной функцией, независимо от того, сколько времени займет цикл for. Во втором примере, однако, setTimeout - это функция, которая вызывает ваш обратный вызов, а setTimeout - это асинхронная функция, которая выполняется после остальной части вашего синхронного кода только потому, что именно так она и должна работать. Другой распространенной асинхронной функцией может быть любой запрос AJAX, и предоставленный ей обратный вызов также будет вызываться после любого другого синхронного кода.