1

点我看看~

promise诞生之前:

因为JS引擎在执行js代码时只分配了一个线程去执行,所以Javascript是单线程的。由于有这个前置设定,前端er在书写代码时绕不开的一件事是就是----如何处理异步,即处理“现在和稍后”关系的问题,事实上我们每一天都在与异步逻辑打交道。

在promise出现之前,前端er基本上都是通过callback的方式来解决“稍后”的问题,例如有经典的“发布-订阅”模式,观察者模式,他们都运用了传入回调函数的高阶函数。vue2.x源码在实现数据双向绑定时就是运用的发布-订阅模式。

我们先来看看三个例子。(例子均在node环境中运行, 其中name.txt中的内容是"kk", age.txt中的内容是10。)

1 . 回调函数(callback)。fs读取文件的先后顺序是不固定的,我们无法判断哪个文件先读取完成。此例实现的是,在完全读取两个文件的内容之后进行某个操作(例如console个啥的)。

let fs = require('fs');

let arr = [];
let after = (times, cb) => {
 return (data) => {
 arr.push(data);
 if (--times === 0) {
 cb(arr)
   }
 }
}

let on = after(2, (arr) => {
 console.log('我是在全部读取了2个文件内容之后打印出来的, ', arr)
})
fs.readFile('name.txt', 'utf8', (err, data) => {
 on(data)
})

fs.readFile('age.txt', 'utf8', (err, data) => {
 on(data)
})

结果:
  我是在全部读取了2个文件内容之后打印出来的, [ 'kk', '10' ]。

说明:
  这种写法的问题在于,需要依靠计数来执行回调函数里面的内容。我们先得这计算出有几个异步操作,然后统计出来在全部的异步操作完成后再执行回调。



2 .发布-订阅模式。订阅的时候添加订阅者,发布的时候执行相应的订阅函数。此例实现的是,在特定的时候emit了某事件,订阅了该事件的回调函数继而执行。


class EventEmitter {
  constructor () {
    this.subs = {}
  }
  on (eventName, cb) {
    if (!this.subs[eventName]) {
      this.subs[eventName] = []
    }
    this.subs[eventName].push((...args) => cb(...args))
  }
  emit (eventName, ...args) {
    if (this.subs[eventName]) {
       this.subs[eventName].forEach(cb => cb(...args))
    } else {
        throw Error(`没有订阅${eventName}这个事件`)
    }
   }
}

const event = new EventEmitter();
let fs = require('fs');
event.on('kk-event', (...args) => {
  fs.readFile('name.txt', 'utf8', (err, data) => {
    console.log('data1', data, ...args)
  })
})
event.on('kk-event', (...args) => {
  fs.readFile('age.txt', 'utf8', (err, data) => {
    console.log('data2', data, ...args)
  })
})
event.emit('kk-event', 123, 456)

结果:

data1 kk 123 456
data2 10 123 456


3 . 观察者模式。它与发布-订阅两者本质是一样的,只不过观察者模式在写法上强调观察者和被观察者之间的关系,而发布-订阅模式则没有这样的关系。此例实现的是,在被观察者的状态发生变化后,观察者执行自己的update方法进行更新。

  
class Subject {
  constructor() {
    this.observers = [];
    this.state = ''; // 假设观察者观察的是被观察者的state
  }
  setState (status) { // 当state变化时出发观察者的update方法
    this.state = status;
    this.notify();
  }
  attach (observer) {
    this.observers.push(observer)  // 与发布-订阅不同的是,这里添加的是一个个观察者实例,这就将被观察者和观察者之间关联了起来
  }
  notify () {
    this.observers.forEach(observe => observe.update()) // 在被观察者状态变化时,调用更新的是观察者的update方法
  }
}

class Observer {
  constructor (name, target) {
    this.name = name;
    this.target = target;
  }
  update () {
    console.log(`通知${this.name},被观察者状态变化,所以观察者${this.name}跟着变化`)
  }
}

let fs = require('fs');
let subject = new Subject();
let observer1 = new Observer('kk1', subject);
let observer2 = new Observer('kk2', subject);
subject.attach(observer1);
subject.attach(observer2);
subject.setState('B');

结果:

通知kk1,被观察者状态变化,所以观察者kk1跟着变化
通知kk2,被观察者状态变化,所以观察者kk2跟着变化

你可能感兴趣的

载入中...