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跟着变化
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。