需知:
1,JavaScript只有一个核心的主线程,但它有存放异步任务的任务队列(task queue)。
2,主线程中是正在运行的同步任务(异步任务开始运行则也会变为同步任务),每次同步任务完成后就会去查看异步任务列表,如果查看时有异步任务达到它的运行条件(比如AJAX请求返回了结果)则把此任务放入主线程中开始执行,主线程的同步任务完成了又会去查看异步任务列表中的每个任务是否达到运行条件,如此往复,直到主线程中的同步任务全部完成以及异步任务列表清空了则脚本结束。
3,回调函数是异步操作最基本的方法,有回调函数的函数将成为异步任务被挂起,(在JavaScript中,回调函数具体的定义为:函数A作为参数(函数引用)传递到另一个函数B中,并且这个函数B执行函数A。我们就说函数A叫做回调函数。如果没有名称(函数表达式),就叫做匿名回调函数。)
4,事件监听也是异步任务。
5,“发布/订阅模式”(又称为:“观察者模式”(observer pattern)),事件完全可以理解成“信号”,如果存在一个“信号中心”,某个任务执行完成,就向信号中心“发布”(publish)一个信号,其他任务可以向信号中心“订阅”(subscribe)这个信号,从而知道什么时候自己可以开始执行。
异步操作的流程控制
1,流程控制之串行执行
// 1,流程控制之串行执行(串行执行是最常见的任务执行流程,比较直观,运行效率稍低于串并行执行,再就是并行执行,但是占用的系统资源是最少的)
var items = [1, 2, 3, 4, 5, 6]; //每个任务的参数
var results = []; //每个任务的结果
function async (arg, callback) { //带有回调函数的函数被视作异步任务,
// 没有达到此异步任务的执行条件将会被引擎放入任务列表被挂起,达到异步任务执行条件执行时会通过回调函数进入主线程,
// 主线程中执行的都是同步任务,异步任务开始执行后也变成的同步任务。
console.log('参数为 ' + arg + ' , 1秒后返回结果');
setTimeout(function () {
callback(arg * 2);
}, 1000);
}
function final(value) { //任务列表执行完成后最终调用的函数
console.log('完成: ', value);
}
// 流程控制函数之串行执行(serial:串行)
function series(item) {
if (item) { //如果参数不为null或undefined,则执行此次异步任务
async (item, function (result) {
results.push(result); //把每次运行结果放入results数组中
return series(items.shift()); //递归series函数以进行下次任务
});
} else { //items中的参数全部用完则任务全部完成,跳出递归,运行final()
return final(results[results.length - 1]);
}
}
series(items.shift()); //开始启动任务列表
2,流程控制之并行执行
// 2,流程控制之并行执行,(并行执行的效率较高,比起串行执行一次只能执行一个任务,较为节约时间。但是问题在于如果并行的任务较多,很容易耗尽系统资源,拖慢运行速度。)
var items = [1, 2, 3, 4, 5, 6];
var results = [];
function async (arg, callback) { //任务
console.log('参数为 ' + arg + ' , 1秒后返回结果');
setTimeout(function () {
callback(arg * 2);
}, 1000);
}
function final(value) { //任务列表完成后执行的函数
console.log('完成: ', value);
}
items.forEach(function (item) { //遍历参数列表,一个参数代表一个任务,回调函数创建异步任务
async (item, function (result) {
results.push(result); //存放每次任务的结果
if (results.length === items.length) { //当结果总数与参数总数相等则任务完成,调用final()
final(results[results.length - 1]);
}
})
});
3,流程控制之串,并行结合
// 3,流程控制之串,并行结合(可以通过调节并行执行的最大任务数量来达到资源与效率的最佳平衡)
var items = [1, 2, 3, 4, 5, 6]; //所有任务的参数
var results = []; //每个任务存放结果的地方
var running = 0; //正在运行的任务数量
var limit = 2; //允许并行执行至多任务数量
function async (arg, callback) { //任务
console.log('参数为 ' + arg + ' , 1秒后返回结果');
setTimeout(function () {
callback(arg * 2);
}, 1000);
}
function final(value) { //任务列表完成后执行的函数
console.log('完成: ', value);
}
function launcher() { //任务发射器(串,并行执行)
while (running < limit && items.length > 0) {//利用循环来挂起异步任务,一次最多挂起 limit 次异步任务,有异步任务执行完成后递归launcher再次用此循环挂起异步任务
var item = items.shift();//取出第一个参数并从参数列表中删除
async (item, function (result) {
results.push(result);//存入每次任务的结果
running--;//正在运行的任务数量-1
if (items.length > 0) {//如果参数列表还有参数则递归launcher()以开始另一个任务
launcher();
} else if (running == 0) {//正在运行的任务数量等于0时结束递归并调用final()
final(results);
}
});
running++;//正在运行的任务数量+1,注意:此行代码为同步任务内容会在异步任务async()前执行,因为同步任务结束后才会检查执行异步任务
}
}
launcher(); //开启任务发射器