【13】查漏补缺——2

一、异步编程的方法:回调函数、事件监听、发布/订阅、Promises对象

方法1:回调函数(callbacks)

优点:简单、容易理解和部署

缺点:

1)不利于代码的阅读和维护,各个部分之间高度耦合(Coupling),流程会很混乱

2)每个任务只能指定一个回调函数

基础用法:

// 主函数
function mainFunction(callback) {
    // do something
    // ...
    // call the callback
    callback('stuff', 'goes', 'here')
} 

// 回调函数
function foo(a, b, c) {
    alert(a + " " + b + " " + c);
} 

// 调用主函数,传入回调函数
mainFunction(foo)

高级用法:

// call
function Thing(name) {
    this.name = name;
}

Thing.prototype.doSomething = function(callback, salutation) {
    // Call our callback, but using our own instance as the context
    callback.call(this, salutation);
} 

function foo(salutation) {
    alert(salutation + " " + this.name);
} 

var t = new Thing('Joe');
t.doSomething(foo, 'Hi'); // Alerts "Hi Joe" via `foo`



// apply
function Thing(name) {
    this.name = name;
}

Thing.prototype.doSomething = function(callback) {
    // Call our callback, but using our own instance as the context
    callback.apply(this, ['Hi', 3, 2, 1]);
} 

function foo(salutation, three, two, one) {
    alert(salutation + " " + this.name + " – " + three + " " + two + " " + one);
} 

var t = new Thing('Joe');
t.doSomething(foo); // Alerts "Hi Joe – 3 2 1" via `foo`

方法2:事件监听

优点:

1)可以绑定多个事件,每个事件可以指定多个回调函数

2)可以“去耦合”(Decoupling),有利于实现模块化

缺点:整个程序都要变成事件驱动型,运行流程会变得很不清晰

实例:

1)原生态的事件绑定函数 addEventListener

const eventOne = function(){
  alert("第一个监听事件");
}

function eventTwo(){
  alert("第二个监听事件");
}

window.onload = function(){
  var btn = document.getElementById("yuanEvent");
  //addEventListener:绑定函数
  btn.addEventListener("click",eventOne);
  btn.addEventListener("click",eventTwo);
}

最后输出:第一个监听事件 和 第二个监听事件

2)当同一个对象使用.onclick的写法触发多个方法的时候,后一个方法会把前一个方法覆盖掉,也就是说,在对象的onclick事件发生时,只会执行最后绑定的方法。而用事件监听则不会有覆盖的现象,每个绑定的事件都会被执行。如下:

window.onload = function(){
  var btn = document.getElementById("yuanEvent");
  btn.onclick =function(){
    alert("第一个事件");
  }
  btn.onclick =function(){
    alert("第二个事件");
  }
  btn.onclick =function(){
    alert("第三个事件");
  }
}

最后只输出:第三个事件,因为后一个方法都把前一个方法覆盖掉了。

参考链接:js事件监听器用法实例详解 - 张华男 - 博客园

方法3:发布/订阅

性质与“事件监听”类似,但是明显优于后者。

1)可以广泛应用于异步编程,它可以代替我们传统的回调函数,我们不需要关注对象在异步执行阶段的内部状态,我们只关心事件完成的时间点。

2)取代对象之间硬编码通知机制,一个对象不必显式调用另一个对象的接口,而是松耦合的联系在一起,虽然不知道彼此的细节,但不影响相互通信。更重要的是,其中一个对象改变不会影响另一个对象。

// 定义发布/订阅模型
const event = {
    // 设置缓存列表,存放订阅者的回调函数列表
    list : [],
    // 设置订阅者
    listen :  function(key , fn){
        if(!this.list[key]){
            this.list[key] = [];
        }
        // 将订阅的消息添加到缓存列表中
        this.list[key].push(fn);
    },

    // 发布事件
    trigger : function() {
        const key = Array.prototype.shift.call(arguments),
                  fns = this.list[key];
        if(!fns ||  fns.length === 0){
             return false;
        }
        for(let i = 0 , fn ; fn = fns[i++];){
             fn.apply(this, arguments);
        }
    },

    // 取消订阅
    remove : function(key , fn){
        const fns = this.list[key];
        // 如果key对应的消息没有订阅过的话,则返回
        if(!fns) {
            return false;
        }
        // 如果没有传入具体的回调函数,表示需要取消key对应消息的所有订阅
        if(!fn) {
            delete this.list[key]; //如果没有后续参数,则删除整个回调数组
        }else {
            for(let i = fns.length - 1 ; i>=0 ;i--) {
                const _fn = fns[i];
                if(_fn === fn) {
                    fns.splice( i, 1);   // 删除订阅者的回调函数
                }
            }
        }
    }
};

const initEvent = function(obj) {
    for(let i in event) {
        obj[i] = event[i];
    }
};

const shoeobj = {};

initEvent(shoeobj);

shoeobj.listen('abcd' , function(a, b) {
    console.log(a);
    console.log(b);
})

shoeobj.listen('efgh' , function(a, b){
    console.log(a);
    console.log(b);
})

shoeobj.trigger('abcd' , 100 ,200);             //   100  200

shoeobj.trigger('efgh' ,  300, 500);           //  300  500

shoeobj.remove('abcd');

shoeobj.trigger('abcd', 20, 50);              //  false

上述的参考链接:Javascript实现简单地发布订阅模式 - 曼施坦因 - 博客园

另一种表达形式:

function Dep() {
  this.subs = []
}
Dep.prototype.addsub = function (sub) {
  this.subs.push(sub)
}
Dep.prototype.notify = function () {
  this.subs.forEach(sub => sub.update())
}
 
function Watcher(fn) {
  this.fn = fn
}
Watcher.prototype.update = function () {
  this.fn()
}
let watcher = new Watcher(function () {
  console.log(1)
})
 
let dep = new Dep()
dep.addsub(watcher)
console.log(dep.subs)
dep.notify()

方法4:Promises对象

Promises对象是CommonJS工作组提出的一种规范,目的是为异步编程提供统一接口。简单说,它的思想是,每一个异步任务返回一个Promise对象,该对象有一个then方法,允许指定回调函数。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值