最近开发了一个适用于iOS上的混合开发库,支持JavaScript
的开发,开发完以后对于JavaScript
中的一些特性有了更加深入的了解。也就有了这篇文章,后续还会陆续写一些其他的关于JavaScript
的文章。
异步编程
一般跟多线程
有关,而我们都知道JavaScript
是单线程执行的,那何来异步
之说?
事实上,但凡在
JS
中执行的代码都是单线程执行的。
然而,我们平常开发JavaScript
的时候经常会提到异步编程
的概念,而且在实际的开发过程中也经常会用到异步编程
。那先说下我们是怎么用的,从最简单的setTimeout
说起,然后一步一步的说到ES8中的async
和await
。
setTimeout、setInterval
setTimeout(function(){
console.log('timeout');
},1000);
复制代码
执行上面的代码1秒后输出timeout
。下面换一种方式,使用setInterval
来实现。
var interval = setInterval(function () {
console.log('timeout');
clearInterval(interval);
}, 1000);
复制代码
上面两种方式实现的效果是一样的,都是在等待1秒后执行function
中的代码。
看到上面的代码,但凡写过其他语言的比如:c++、java、OC等的,都知道这其实就是一个定时器,而setTimeout
稍微特殊点,只会执行一次,而setInterval
能够执行多次,直到显示取消为止。
举上面的例子主要是说明,在JavaScript中,setTimeout
和setInterval
可以实现类似异步的效果。
不同的语言对于定时器的实现方式有可能不一样,甚至同一种语言,提供多种不同的定时器API,就拿
Objectiv-c
来说,起码有三种方式实现定时器的效果,但是不管什么语言,用什么方式来实现,工作原理差不多,
就是当
定时器
获取到某个信号
的时候,在创建该定时器的线程中插入任务。这样的过程可以理解为线程调度。因为定时信号
不一定在你回调的线程上触发的,该信号
有可能直接由硬件中断
产生,也有可能是软件模拟
产生,但是在使用的时候都会在创建的线程获得回调。
因此,定时器本质上还是单线程的,跟异步编程
没关系。我举个例子你就明白了
var t1 = new Date();
setTimeout(function(){
var t2 = new Date();
console.log('diff:'+(t2-t1));
},1000);
while (new Date() - t1 < 2000) {
}
复制代码
上面的代码你觉得是输出1000
呢?还是2000
?
实际的代码输出是diff:2000
,具体是2000多少要看实际情况。举这样的例子说明什么呢,正是说明setTimeout
还是串行执行的,你可以理解为见缝插针
方式。因此setTimeout
压根就算不上异步
。
一个残酷的现实是,javascript
中不存在真的异步
,一切异步
都是假象。
所有的JS代码都是在同一个线程上执行的,因此不存在多线程的概念,也就不存在真正的异步编程。
其实某些浏览器支持
worker
API,算是真的多线程。但是本篇不做介绍。
既然本篇的主题是异步编程
,那么就算是假象,也得继续了。
XMLHttpRequest
我记得JS的异步编程的概念被提出来是从XMLHttpRequest
(Ajax)开始的,而这里的异步指的只是实际的http请求
是在非JS线程执行的,而请求完毕
后的回调还是在JS线程执行的。这样看起来是异步的,但是我们要注意到,http异步请求
不是在JS线程上执行的,而是由浏览器负责的,跟JS没关系。但是,正是因为有了这样的模型,才使得一些原本无法实现的功能变得可能。