什么是异步?
异步与同步概念简介
背景
JavaScript 是一种单线程语言。在任何时刻,只有一个指令被 JavaScript 程序处理。程序在同一时间仅使用一个处理器。下面的代码是一个例子。
1for (var i = 0; i < 100; i++){ 2 process.stdout.write(`${i} `); 3}
在上述代码中,数字通过循环逐个打印出来。整个程序运行在计算机的处理器上。计算的实际速度取决于处理器的速度。同一时间只能执行一条指令。所有指令按顺序执行,即前一条指令必须完成后,下一条才能开始。
如果某个函数在执行时并不占用处理器?如果函数中的某条指令正在等待网络数据或磁盘读取呢?此时,若处理器能在等待期间执行其他工作,效率将大幅提升!即使只有一个处理器,性能也会显著提高!
异步编程简介
让我们来了解异步性的概念。当程序是异步的时,处理器可以在不等待阻塞代码的情况下处理多个任务。
阻塞代码
阻塞代码是指在等待与处理器无关的任务完成时,使处理器处于空闲状态的代码。以下情况都属于阻塞代码:
- 等待网络获取数据
- 等待磁盘完成数据操作
- 在程序中调用某些睡眠函数
- 等待用户输入数据
下面是一个示例代码:
1function complete(){ 2 console.log("function completed!"); 3 return; 4} 5setTimeout(complete, 2000.0); // 2000 = 2秒的空闲时间
这里,setTimeout(func, x) 函数会在经过 x 毫秒的空闲时间后,调用函数 func。在我们的代码中,将 complete 函数作为参数传给 setTimeout。这会延迟两秒后打印出字符串 'function completed!'。程序将在两秒钟内保持空闲,之后再调用 complete 函数。注意,整个程序的执行时间超过了两秒。
异步行为
由于 JavaScript 是异步的,它不会等待阻塞代码的执行。相反,它会在等待阻塞代码完成的同时,继续执行其他可计算的任务。请看下面的代码:
1function complete(){ 2 console.log("function completed!"); 3 return; 4} 5setTimeout(complete, 2000.0); // 2000 = 2秒的空闲时间 6console.log("Calling after setTimeout");
与之前相同,我们在阻塞函数 setTimeout 之后添加了新的代码。JavaScript 仍然会执行这条语句。尽管 setTimeout 导致程序“阻塞”,但第6行的打印语句依然被执行。这种打印顺序的特殊性,正是由于 JavaScript 支持异步编程。