Стек вызовов (call stack) в javascript




Как работает JS



Пример 1. Стек и порядок вызова ф-ций

	function sum(a, b) {
		return a + b
	}

	function pow(a) {
		return a * a
	}


	pow(2)
	sum(2, 3)


	/*

		ПОРЯДОК ВЫЗОВА Ф-ЦИЙ БУДЕТ ВЫГЛЯДЕТЬ ТАК

			pow
			sum

		Т.Е. СНАЧАЛА БУДЕТ ВЫЗВАНА Ф-ЦИЯ pow, А ТОЛЬКО ЗАТЕМ sum
		НЕСМОТРЯ НА ТО, ЧТО sum ОПИСАНА ПЕРВОЙ					

	*/



Асинхронные ф-ции


Это ф-ции, результат действия которых будет известен спустя какое-то время после их вызова

Примеры асинхронных ф-ций: setTimeout, setInterval, и ф-ция - коллбек события readystatechange AJAX'а


Асинхронные ф-ции попадают в стек вызова последними, независимо от того, где они вызваны в коде


Пример 2. Асинхронные ф-ции и порядок вызова

	function print(result){
		console.log('Результат: ', result)
	}

	function sum(a, b) {
		return a + b
	}

	function pow(a) {
		return a * a
	}

	setTimeout(() => {
		print('ПРОСТО ТЕСТ')
	}, 0);
	print(pow(2))
	print(sum(2, 3))


	/*

		ТЕПЕРЬ ПОРЯДОК ВЫЗОВА Ф-ЦИЙ БУДЕТ ВЫГЛЯДЕТЬ ТАК

			print(pow(2))
			print(sum(2, 3))
			print('ПРОСТО ТЕСТ')

		print('ПРОСТО ТЕСТ') В setTimeout БУДЕТ ВЫЗЫВАТЬСЯ ПОСЛЕДНЕЙ, 
		НЕСМОТРЯ НА ТО, ЧТО ОНА УКАЗАНА ПЕРВОЙ				

	*/


Пример 3. Несколько асинхронных ф-ции

	function print(result){
		console.log('Результат: ', result)
	}

	function sum(a, b) {
		return a + b
	}

	function pow(a) {
		return a * a
	}

	setTimeout(() => {
		print('ПОТОМ ЭТО')
	}, 1000);
	setTimeout(() => {
		print('СНАЧАЛА ЭТО')
	}, 0);
	print(pow(2))
	print(sum(2, 3))


	/*

		ТЕПЕРЬ ПОРЯДОК ВЫЗОВА Ф-ЦИЙ БУДЕТ ВЫГЛЯДЕТЬ ТАК

			print(pow(2))
			print(sum(2, 3))
			print('СНАЧАЛА ЭТО')  - setTimeout = 0
			print('ПОТОМ ЭТО')  - setTimeout = 1000
		

	*/




Проблема с AJAX


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

Пример 4. Обычный AJAX'а запрос на удалённый сервер. Начинающий программист пытается получить данные дальше по коду, где стоит print(0) и не получает их. В то время, как надо получать данные в ф-ции-коллбеке, где стоит print(1)

	function print(result){		
		console.log(result)
	}


	let data


	// AJAX ЗАПРОС
	const request = new XMLHttpRequest()

	// СОБЫТИЕ readystatechange - СРАБАТЫВАЕТ, КОГДА ИЗМЕНЯЕТСЯ СТАТУС ОТПРАВЛЕННОГО НАМИ ЗАПРОСА
	request.addEventListener('readystatechange', (e) => {
		data = e.target.responseText
		try {
			if (e.target.readyState === 4 && e.target.status === 200) {
				print('1')
				// ПРАВИЛЬНО - ПРИ УСПЕШНОМ ЗАПРОСЕ, ДАННЫЕ БУДУТ ПОЛУЧЕНЫ И ВЫВЕДЕНЫ
				print(JSON.parse(data))
			}
		} catch (error) {
			// error
		}
		
	})

	request.open('GET', `https://jsonplaceholder.typicode.com/todos/1`)
	request.send()



	console.log('===================')
	print('0')
	// ОШИБКА - ДАННЫЕ ВЫВЕДЕНЫ НЕ БУДУТ
	print(data)
	console.log('===================')


	/*

		ПОРЯДОК ВЫЗОВА

			print(0)
			print(data)
			print(1)
			print(JSON.parse(data))
		

	*/


С точки зрения цикла выполнения JS, коллбек-ф-ция AJAX запроса попадает в стек уже после того, как туда попали все синхронные ф-ции кода. То есть снача отработает print(0), а затем, через какое-то время print(1)



Упражнения


1. Создать 4 ф-ции, которые должны выводить в консоль какую-то строку. Вывести 2 из них обычным способом, а 2 - внутри setTimeout с разными таймерами. Ф-ции с setTimeout вызвать в коде первыми. Посмотреть в консоли результат и написать, какая ф-ция вызывается в каком порядке