JS:同步、异步与回调

位置: 首页 > 前端三套件
[发布: 2023.9.10  作者: Admin  阅读: 116]

JS以单线程执行代码。这会让很多人认为JS代码是按顺序自上而下一句接着一句依次执行的。这个说法也没错,试看:

<div id="mydiv">请稍候……</div>

<script>

let 函数1 = () => {
	mydiv.innerText += 'n我是函数1';
};

let 函数2 = () => {
	mydiv.innerText += 'n我是函数2';
};

函数1();
函数2();

</script>

执行函数1和函数2,其结果是我们预期的:

	我是函数1
	我是函数2

不过,当函数1要做的事情很耗时,运行结果会如何呢?我们给函数1加一个定时器,假装它要做的事情耗时1秒钟:

<div id="mydiv">请稍候……</div>

<script>

let 函数1 = () => {
	setTimeout( () => {
		mydiv.innerText += 'n我是函数1';
	}, 1000);
};

let 函数2 = () => {
	mydiv.innerText += 'n我是函数2';
};

函数1();
函数2();

</script>

执行结果:

	我是函数2
	我是函数1

这次,结果和上一次相反。原因:JS运行代码的次序虽然是自上而下一句一句交付执行,但这仅限于分派任务,执行结果的快慢要根据各个代码块处理事件的耗时情况而定。上例,函数1延时一秒然后再处理输出事件,可JS的流水线节奏紧凑,该事件的代码交付执行执行之后,余下的代码紧跟着交付。函数2没有延时输出,所以它放到执行线程之后立马就出结果,而函数1则等完自己那一秒时间过了才输出自己的处理结果。

这里面,要牵出两个概念,同步与异步。分派任务,即代码模块交付执行,虽然也确实存在先后次序问题,可是这个环节都是一步到位、瞬间完成,用“秒杀”这个新词汇都不能精准描述这个环节,因此这个环节可视作是同步的。JS流水线是个单线程构架,各个工位上,如果大家也能在相同的时间内完成各自生产环节的工作,那是很理想的状态,通常也都是这样,但是,非常遗憾,一些应用场景下,有些工位上工作复杂、处理加工环节耗时不确定,这就来大麻烦了:我们知道,如果事件间有前后依赖关系(生产线上都这样),前面环节结果尚未出来,后面的环节就不能进行,否则出错或根本无法干下去。异步概念因此引出:各个代码块运行耗时的不同,运行结果不能在同一时间拿到,这可以叫做运行结果异步。运行结果异步完成的现象不是好现象,比如上例,函数1运行耗时较长,假如函数2的运行需要函数1的结果或结果的一部分,则函数2的运行结果就不靠谱,在JS里运行出错,余下的代码块拒绝运行。为避免出错,我们应有异步交付执行的机制,等上一个环节出了结果,再去运行下一个环节。我们改进一下上面的代码:

<div id="mydiv">请稍候……</div>

<script>

let 函数1 = (回调) => {
	setTimeout( () => {
		mydiv.innerText += 'n我是函数1';
		回调();
	}, 1000);
};

let 函数2 = () => {
	mydiv.innerText += 'n我是函数2';
};

函数1(函数2);

</script>

函数1加了一个“回调”参数,这不是普通的参数,它实际上是一个函数,看它在函数1的调用方式 “回调();” 就知道。回调函数写在定时器里,放在输出结果语句之后。然后,这个非常重要,函数1和函数2不是依次运行,而是,函数1把函数2作为自己的运行参数运行,函数2此时就是函数1里的“回调”那个参数。这就彻底解决了异步问题:函数2的执行放在函数1执行结果出来之后再执行。

不知不觉,我们已经引入了JS的高阶函数,函数1就是,因为它有回调函数。以其他函数做参数的函数就叫高阶函数。而回调函数(callback)很多编程语言也都存在,在JS世界,回调函数主要用于解决环节运行结果异步的问题,一般就命名为 callback 而不是我们在函数1里的“回调”。


前一篇: JS:监视元素宽高变化
下一篇: 曲线进度条的实现

发表评论:

  
 

评论列表 [0条]

Copyright © 2023 All Right Reserved 马黑PHP文章管理整站系统v1.8
联系我们: gxblk@163.com