你与弄懂promise之间可能只差这篇文章(一)

9次阅读

共计 2344 个字符,预计需要花费 6 分钟才能阅读完成。

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 跟着变化

正文完
 0