异步
出现的原因
JavaScript语言的执行环境是"单线程"
所谓"单线程",就是指一次只能只能完成一件任务。如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,以此类推。
单线程这种模式好处是实现起来比较简单,执行环境单一。坏处是只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。常见的浏览器无响应(假死),往往就是因为某一段JavaScript代码长时间运行(比如死循环),导致整个页面卡在这个地方,其他任务无法执行。
为了解决这个问题,JavaScript语言将任务的执行模式分成两种,同步和异步。
异步的使用场景
Ajax操作
异步编程的实现
回调函数
优点:简单丶很容易理解和部署。
缺点:不利于阅读和维护,各部分之间高度耦合,流程会很混乱,而且每个任务只能指定一个回调函数。
代码实现// 假如有两个函数f1和f2 后者等待前者的执行结果
// 如果f1是一个很耗时的任务,可以考虑改写f1,把f2写成f1的回调函数
function f2© {
console.log(c, ‘hehe’);
}
function f1(callback) {
setTimeout(function() {
// f1任务代码
console.log(‘f1执行完’);
callback(‘f2开始执行’, ‘hehe’);
}, 1000);
}
f1(f2);
复制代码
事件监听
代码实现
优点:比较容易理解,可以绑定多个事件,每个事件可以指定多个回调函数,而且可以"去耦合",有利于实现模块化。
缺点:整个程序都变成事件驱动,运行流程会变得很不流畅。f1.on(‘done’, f2);
复制代码
发布/订阅
定义:我们假定,存在一个"信号中心",某个任务执行完成,就向信号中心"发布"一个信号,其他任务可以向信号中心"订阅"整个信号,从而知道什么时候自己可以开始执行。这就叫做"发布/订阅模式",又称"观察者模式"。
优点:
这种方法的性质和"事件监听"类似,但是明显优于后者。因为我们可以通过查看"消息中心",了解存在多少信号丶每个信号有多少订阅者,从而监控程序的运行。
一是时间上的解耦,而是对象上的解耦
即可用于异步编程中,也可以用帮助我们完成更松耦合的代码编写
缺点:
创建订阅者本身需要消耗一定的时间和内存
当订阅一个消息时,也许次消息并没有发生,但这个订阅者会始终存在内存中
观察者模式弱化了对象之间的联系,这本是好事,但如果过度使用,对象与对象之间的联系也会隐藏的很深,会导致项目的难以追踪维护和理解。
使用场景
DOM事件
自定义事件
代码实现function Event() {
// 存储不同的事件类型对应不同的处理函数,保证后续emmit可以执行。
this.cache = {};
}
// 绑定事件
Event.prototype.on = function(type, handle) {
if(!this.cache[type]) {
this.cache[type] = [handle];
}else {
this.cache[type].push(handle);
}
}
// 事件触发
Event.prototype.emmit = function() {
var type = arguments[0],
arg = [].slice.call(arguments, 1);
for(var i = 0; i < this.cache[type].length; i++) {
this.cache[type][i].apply(this, arg);
if(this.cache[type][i].flag) {
this.cache[type].splice(i, 1);
if(this.cache[type][i].flag) {
this.cache[type].splice(i, 1);
}
}
}
}
// 解除某个事件类型
Event.prototype.empty = function(type) {
this.cache[type] = [];
}
// 解除某个事件
Event.prototype.remove = function(type, handle) {
this.cache[type] = this.cache[type].filter((ele) => ele != handle);
}
// 绑定一次事件
Event.prototype.once = function(type, handle) {
if(!this.cache[type]) {
this.cache[type] = [];
}
// 做标记
handle.flag = true;
this.cache[type].push(handle);
}
function detail1(time) {
console.log(‘overtime1’ + time);
}
function detail2(time) {
console.log(‘overtime2’ + time);
}
var oE = new Event();
oE.on(‘over’, detail1);
oE.on(‘over’, detail2);
oE.emmit(‘over’, ‘2019-11-11’);
oE.remove(‘over’, detail2);
oE.emmit(‘over’, ‘second-11-11’);
复制代码
Promise对象
定义
是CommonJS工作组提出的一种规范,目的是为了异步编程提供统一接口。
简单来说,它的思想就是,每一个异步任务都返回一个Promise对象,该对象有一个then方法,允许指定回调函数。
优点:
回调函数变成链式写法,程序的流程可以看得很清楚,而且有一整套流程的配套方法。
而且,它还有一个前面三种方法都没有