JS中的同步与异步问题

JS中的同步与异步,解释的最准确的一段话:

JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊。

JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?

所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。

为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。

单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。于是就有一个概念,任务队列。

如果排队是因为计算量大,CPU忙不过来,倒也算了,但是很多时候CPU是闲着的,因为IO设备(输入输出设备)很慢(比如Ajax操作从网络读取数据),不得不等着结果出来,再往下执行。

JavaScript语言的设计者意识到,这时主线程完全可以不管IO设备,挂起处于等待中的任务,先运行排在后面的任务。等到IO设备返回了结果,再回过头,把挂起的任务继续执行下去。

于是,所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行.

外加一幅图理解程序的执行顺序:
在这里插入图片描述

一 异步

在JS中那些是异步操作呢?

//js中的异步操作(语句)
1. 定时器
2. ajax
3. 事件函数

1.1 定时器

js中的定时器,是异步代码,也就是说程序会先执行同步代码,当定时器时间到了之后,通知主线程来执行同步代码.

function fun() {
		var name = '李四';
		setTimeout(function () {
			name = '张三';
		},2000)
		return name;
}
	var name = fun();
	console.log(name);

我们希望代码是先打印出张三,但是最终只会打印李四.因为定时器是异步的,当定时器时间到来时,函数都已经调用完了.所以打印的一直是默认值.

1.2 ajax

ajax同样是异步代码,本例打印出来的结果同样是李四,道理与定时器一样.

function fun() {
		var name = '李四';
		$.ajax({
			url:'url',
			success:function (data) {
				name = data;
			}
		})
		return name;
	}
	var name = fun();
	console.log(name);

1.3 事件函数

事件同样具有异步性,因为事件时等待触发之后,才会执行,如果一开始主线程上就有同步代码,同样会先执行同步代码,直到触发后才会执行,但是这里函数调用时先于事件触发的,函数调用完后,便不会再去调用了,所以打印的还是李四.

function fun() {
		var name = '李四';
		div.addEventListener('click', function () {
			name = '张三';
		});
		return name;
	}
	var name = fun();
	console.log(name);

二 怎样访问异步代码里面的变量

如果我就要拿上面代码里的name变量,我们可以利用回调函数来实现.因为异步函数里面是不能使用return的.

//callback是一个回调函数
function fun(callback) {
	var name;
	setTimeout(function () {
		name = '张三';
		callback(name);
	},1000)

}
//fun函数的参数就是一个callback的实际参数
fun(function (name) {
	console.log(name);
});

上述代码相当于:

//callback是一个回调函数
function fun() {
	var name;
	setTimeout(function () {
		name = '张三';
		//调用callback函数
		callback(name);
		function callback  (name) {
			console.log(name);
		}
	},1000)
}
//调用函数
fun();

总结一下关于变量的操作:

// 1. 什么时候使用全局变量 : 就是这个变量在后面很多地方都要用到就会使用全局变量
// 2. 什么时候使用return返回值 : 这个函数是同步执行能够拿到返回值
// 3. 什么时候使用回调函数  :  这个函数在异步执行的时候就要使用回调函数来获取私
有变量

案例:回调函数加ajax的事件响应函数

function sendAjax (pageId,callback){
		var baseURL = 'http://localhost:9090/api/';
		$.ajax({
			url : baseURL + 'getmoneyctrl',
			dataType : 'json',
			data : {
				pageid : pageId
			},
			success : function(obj) {
				//obj是ajax响应成功返回的数据
				callback(obj);
			} 
		})
	}
}

//回调函数
function callback (obj) {
	doSomething.....
}

上面的代码用在,发送ajax的路径一直,但是对于返回数据操作却不同的地方.

猜你喜欢

转载自blog.csdn.net/weixin_42839080/article/details/82989200