你们都知道,js是一个单线程的语言(只有一个线程来执行js函数),因此若是某一个函数执行任务耗时比较长的话,就会形成阻塞,使得后续任务一直处于等待状态。html
1、阻塞示例ajax
function f1(){ for (var i = 0; i < 2000; i++) { console.log('f1'); } } function f2(){ console.log('f2'); } f1(); f2();
f一、f2依次执行,控制台打印:编程
2、setTimeout函数异步
function f1(){ for (var i = 0; i < 2000; i++) { setTimeout(()=>{ console.log('f1'); },500); } } function f2(){ console.log('f2'); } f1(); f2();
f一、f2依次执行,控制台打印:异步编程
看上去好像先执行了f2,后续才执行了f1,其实否则,实际上依旧是先执行f1,再执行f2,没有实现异步,f一、f2依然是同步的(不要质疑,我说的是f1,f2没有实现异步)。函数
首先执行f1,利用for循环开启2000个定时器;spa
而后执行f2,打印字符串;线程
时间到了,2000个定时器触发。3d
注意:定时器到了时间触发以后,也并非当即执行的,这要取决于js单线程内有没有还有没有函数正在运行,咱们来看一个例子:code
function f8(){ for (let i = 0; i < 2000; i++) { setTimeout(()=>{ console.log('f8'+i); },50); } } function f9(){ for (let j = 0; j < 2000; j++) { console.log('f9'+j); } } f8(); f9();
咱们看到,定时器的时间参数值很小,50ms,依然是依次执行f八、f9;
能够看到,虽然定时器先开启,而且第一个定时器在50ms以后,理当执行,可是此时发现,f9这个函数一直在执行任务,占据了js线程,因此这些定时器任务只好乖乖地等f9的循环走完,才开始执行。
因此说js声明的全部变量/函数都是同步的(ES6的Promise实例化以后能够有一个当即执行代码块,可视为同步任务);
可是咱们能够经过各类方式开启一个异步任务(例如setTimeout函数的callback,Promise对象的then(callback)回调);
3、同步队列、异步队列
由于js是单线程的,因此任务会在线程上依次逐个执行;
在主线程上的任务队列,这些任务是同步的,他们会按前后依次执行;
另外还有一种任务队列,暂且称他们为异步任务队列,顾名思义,他们是异步任务,异步任务的特色是,必须等主线程上的任务没有了以后,才依次进入主线程执行。
来个例子感觉一下:
ok,根据上图,咱们能够知道,上述代码中,任务创建的顺序为:
同步任务:Promise实例化、f1的第一行打印、f1的for循环开启1000个定时器、5个f2的字符串打印;
异步任务:Promise的then回调、1000个定时器回调。
再根据以前的任务执行顺序分析,先不要看下面的执行结果,在心中想一下控制台的打印结果。
关于上述所提到的同步队列和异步队列(只是一种形象的说法),详情请戳下篇博客!
4、异步实现
从上面咱们能够发现,setTimeout(定时器)的回调是异步任务,这些个任务是直接放到异步任务队列中等待的。
咱们熟知的ajax之因此是异步,彻底得益于XMLHttpRequest这个异步对象。
ES6为方便异步编程,直接提供了一个异步对象Promise,使得异步编程变得简单。