同步与异步
提到异步,脑子里跳出来的第一个词是ajax异步加载,异步即相互不影响,总感觉异步离我很远,直到去看了相关的文档,瞬间恍然大悟。
1. 单线程
首先了解下,JavaScript是采用的是单线程模型(JavaScript 同时只能执行一个任务,其他任务都必须在后面排队等待)
2. 同步任务和异步任务
程序里面的所有任务,简单分成两类:同步任务(synchronous)和异步任务(asynchronous)。
同步任务
是那些没有被引擎挂起、在主线程上排队执行的任务。只有前一个任务执行完毕,才能执行后一个任务。
异步任务
是那些被引擎放在一边,不进入主线程、而进入任务队列的任务。只有引擎认为某个异步任务可以执行了(比如 Ajax 操作从服务器得到了结果),该任务(采用回调函数的形式)才会进入主线程执行。排在异步任务后面的代码,不用等待异步任务结束会马上运行,也就是说,异步任务不具有”堵塞“效应。
(搬运过来的概念)
口语化,同步就是一个一个地执行,后面的在排队等待。异步就是一个在执行,异步不在等待的队伍中,当达到了触发异步的点,异步被调用执行,异步执行完了继续按照原来的顺序来执行任务。
3. 任务队列和事件循环
首先,主线程会去执行所有的同步任务。等到同步任务全部执行完,就会去看任务队列里面的异步任务。如果满足条件,那么异步任务就重新进入主线程开始执行,这时它就变成同步任务了。等到执行完,下一个异步任务再进入主线程开始执行。一旦任务队列清空,程序就结束执行。
异步任务的写法通常是回调函数。一旦异步任务重新进入主线程,就会执行对应的回调函数。如果一个异步任务没有回调函数,就不会进入任务队列,也就是说,不会重新进入主线程,因为没有用回调函数指定下一步的操作。
(概念有点晕)
4. 异步操作的几种模式
4.1 回调函数
同步:
function f1() {
// ...
}
function f2() {
// ...
}
f1();
f2();
异步:
function f1(callback) {
// ...
callback();
}
function f2() {
// ...
}
f1(f2);
4.2 事件监听
异步的执行不取决于代码的顺序,而取决于某个事件(触发它的点)是否发生。
f1.on('done', f2);
function f1() {
setTimeout(function () {
// ...
f1.trigger('done');
}, 1000);
}
4.3 发布/订阅
可以理解为事件监听的另一种形式。
优势:可以通过查看“消息中心”,了解存在多少信号、每个信号有多少订阅者,从而监控程序的运行。
jQuery.subscribe('done', f2); //订阅
function f1() {
setTimeout(function () {
// ...
jQuery.publish('done'); //订阅
}, 1000);
}
- 异步操作的流程控制
如果有多个异步操作,就存在一个流程控制的问题:如何确定异步操作执行的顺序,以及如何保证遵守这种顺序。
5.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);
}
function series(item) {
if(item) {
async( item, function(result) {
results.push(result);
return series(items.shift());
});
} else {
return final(results[results.length - 1]);
}
}
series(items.shift());
(一开始看了半个钟没get到点)
5.2 并行执行
流程控制函数也可以是并行执行,即所有异步任务同时执行,等到全部完成以后,才执行final函数。
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(results[results.length - 1]);
}
})
});
看完才知道,原来我日常写的都是异步!!只是没有一个很清晰的概念,有回调函数的都是异步啦(这样立即应该没错吧)
5.3 并行与串行的结合
所谓并行与串行的结合,就是设置一个门槛,每次最多只能并行执行n个异步任务,这样就避免了过分占用系统资源。
(看到这感觉可以忽略掉后面的代码了)
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) {
var item = items.shift();
async(item, function(result) {
results.push(result);
running--;
if(items.length > 0) {
launcher();
} else if(running == 0) {
final(results);
}
});
running++;
}
}
launcher();
记录此文,方便日后翻阅有关异步的相关知识点,大部分内容来源于网络。
传送门:
https://wangdoc.com/javascript/async/general.html#%E5%BC%82%E6%AD%A5%E6%93%8D%E4%BD%9C%E7%9A%84%E6%B5%81%E7%A8%8B%E6%8E%A7%E5%88%B6