js 中异步处理

github 的地址 欢迎 star!
俗话说异步是万恶之源,但是没有异步是万万不行的

问题引出:

  • 在 react 里面的一个父组件中通过发起请求获取后台的数据,然后传递给子组件,发现在子组件用到该数据时并没有触发相应的改变。打开谷歌控制台发现 xhr 请求还在 pending。因此就总结了异步的处理。

js异步的几种方式

1.回调

回调函数的优点是简单、容易理解和部署,缺点是不利于代码的阅读和维护,各个部分之间高度耦合(Coupling),流程会很混乱,而且每个任务只能指定一个回调函数。

2.事件监听

参考阮老师的解释

3. 发布/订阅

来自于--JavaScript 设计模式与开发实践

const event = {

  clientList: [],

  listen(key, fn) {
    if (!this.clientList[key]) {
      this.clientList[key] = [];
    }
    this.clientList[key].push(fn); // 订阅的消息添加进缓存列表
  },

  trigger() {
    let key = Array.prototype.shift.call(arguments);
    let fns = this.clientList[key];

    if (!fns || fns.length === 0) {
      return false;
    }
    for (let i = 0, fn; fn = fns[i++];) {
      fn.apply(this, arguments);
      //arguments 是trigger带上的参数
    }
  },

  //  移除事件
  remove(key, fn) {
    let fns = this.clientList[key];
    if(!fns) return false;
    if(!fn){
      fns && (fns.length = 0);
    }else{
      for(let i = fns.length -1; i>=0; i--){
        let _fn = fns[i];
        if(_fn == fn){
          fns.splice(i,1); //删除订阅者的回调函数
        }
      }
    }
  }
};
复制代码

上面在使用的时候还没有解决命名冲突的问题。具体使用的代码应该充分考虑

// 解决命名冲突问题
var globalEvent = (function () {
  var global = this, Event,
    _default = 'default';

  Event = function () {
    var _listen,
      _trigger,
      _remove,
      _slice = Array.prototype.slice,
      _shift = Array.prototype.shift,
      _unshift = Array.prototype.unshift,
      namespaceCache = {},
      _create,
      find,
      each = function (ary, fn) {
        var ret;
        for (var i = 0, l = ary.length; i < l; i++) {
          var n = ary[i];
          ret = fn.call(n, i, n);
        }
        return ret;
      };

    _listen = function (key, fn, cache) {
      if (!cache[key]) {
        cache[key] = [];
      }
      cache[key].push(fn);
    };

    _remove = function (key, cache, fn) {
      if (cache[key]) {
        if (fn) {
          for (var i = cache[key].length; i >= 0; i--) {
            if (cache[key][i] === fn) {
              cache[key].splice(i, 1);
            }
          }
        } else {
          cache[key] = [];
        }
      }
    };

    _trigger = function () {
      var cache = _shift.call(arguments),
        key = _shift.call(arguments),
        args = arguments,
        _self = this,
        ret,
        stack = cache[key];

      if (!stack || !stack.length) {
        return;
      }

      return each(stack, function () {
        return this.apply(_self, args);
      });
    };

    _create = function (namespace) {
      var namespace = namespace || _default;
      var cache = {},
        offlineStack = [], //离线事件
        ret = {
          listen: function (key, fn, last) {
            _listen(key, fn, cache);
            if (offlineStack === null) {
              return;
            }
            if (last === 'last') {
              offlineStack.length && offlineStack.pop()();
            } else {
              each(offlineStack, function () {
                this();
              });
            }
            offlineStack = null;

          },
          one: function (key, fn, last) {
            _remove(key, cache);
            this.listen(key, fn, last);
          },
          remove: function (key, fn) {
            _remove(key, cache, fn);
          },
          trigger: function () {
            var fn,
              args,
              _self = this;

            _unshift.call(arguments, cache);
            args = arguments;
            fn = function () {
              return _trigger.apply(_self, args);
            };

            if (offlineStack) {
              return offlineStack.push(fn);
            }
            return fn();
          }
        };
      return namespace ? (namespaceCache[namespace] ?
          namespaceCache[namespace] : namespaceCache[namespace] = ret
      ) : ret;
    };

    return {
      create: _create,
      one: function (key, fn, last) {
        var event = this.create();
        event.one(key, fn, last);
      },
      remove: function (key, fn) {
        var event = this.create();
        event.remove(key, fn);
      },
      listen: function (key, fn, last) {
        var event = this.create();
        event.listen(key, fn, last);
      },
      trigger: function () {
        var event = this.create();
        event.trigger.apply(this, arguments);
      }
    };
  }();
  return Event;

})();
// 运用了虚拟代理,
复制代码

此时,可能就会问到,发布订阅和观察者模式的区别:引入知乎里面的回答:

发布订阅模式属于广义上的观察者模式

发布订阅模式是最常用的一种观察者模式的实现,并且从解耦和重用角度来看,更优于典型的观察者模式

发布订阅模式多了个事件通道

  1. 观察者模式中,观察者需要直接订阅目标事件;在目标发出内容改变的事件后,直接接收事件并作出响应
  2. 发布订阅模式中,发布者和订阅者之间多了一个发布通道;一方面从发布者接收事件,另一方面向订阅者发布事件;订阅者需要从事件通道订阅事件

具体的总结:

  1. 在观察者中,观察者监听着目标对象,目标对象也管理保存着它的观察者们,发布订阅中,发布者和订阅者之间是独立(不需要互相认识),它们只在消息队列或代理情况才能通信
  2. 在发布订阅模式中,组件是松散连接的,观察者与之相反
  3. 观察者模式执行大多是通过同步的方法,例如,当一些事件触发时,目标对象(Subject)就会调用观察者的方法。发布订阅模式执行大多是异步的(使用消息队列)
  4. 观察者模式需要在单个应用程序地址空间上实现,相对应的,发布-订阅更像交叉应用模式。

观察者模式像rxjs,可以看一下 rxjs 原理解析

4.Promise/Async

每一个异步任务返回一个 Promise 对象,该对象有一个 then 方法,允许指定回调函数。 在 promise 里面主要作用就是将毁掉嵌套书写为链式调用的方式,其中可以给 then 方法传递函数,将多个依赖异步拆分,(方便维护,可扩展性)。对于需要同时得到结果的多个异步,就可以使用 Promise.all 的方法

如果有错误或者不严谨的地方,请务必给予指正,十分感谢!

参考:

  1. www.ruanyifeng.com/blog/2016/0…
  2. zhuanlan.zhihu.com/p/32911022
  3. facebook.github.io/react
  4. www.jackcallister.com/2015/01/05/…
  5. ryanclark.me/getting-sta…
  6. www.zhihu.com/question/23…
  7. hackernoon.com/observer-vs…
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值