前言
异步编程从早期的 callback、事件发布/订阅模式到 ES6 的 Promise、Generator 在到 ES2017 中 async,看似风格迥异,但是还是有一条暗线将它们串联在一起的,就是希望将异步编程的代码表达尽量地贴合自然语言的线性思维。
以这条暗线将上述几种解决方案连在一起,就可以更好地理解异步编程的原理、魅力。
├── 事件发布/订阅模式 <= Callback
├── Promise <= 事件发布/订阅模式
├── Async、Await <= Promise、Generator
事件发布/订阅模式 <= Callback
这个模式本质上就是回调函数的事件化。它本身并无同步、异步调用的问题,我们只是使用它来实现事件与回调函数之间的关联。比较典型的有 NodeJS 的 events 模块
const { EventEmitter } = require('events')const eventEmitter = new EventEmitter()// 订阅eventEmitter.on("event", function(msg) {console.log("event", msg)})// 发布eventEmitter.emit("event", "Hello world")
那么这种模式是如何与 Callback 关联的呢?我们可以利用 Javascript 简单实现 EventEmitter,答案就显而易见了。
class usrEventEmitter {constructor () {this.listeners = {}}// 订阅,callback 为每个 event 的侦听器on(eventName, callback) {if (!this.listeners[eventName]) this.listeners[eventName] = []this.listeners[eventName].push(callback)}// 发布emit(eventName, params) {this.listeners[eventName].forEach(callback => {callback(params)})}// 注销off(eventName, callback) {const rest = this.listeners[eventName].fitler(elem => elem !== callback)this.listeners[eventName] = rest}// 订阅一次once(eventName, callback) { const handler = function() {callback()this.off(eventName, handler)}this.on(eventName, handler)}}
上述实现忽略了很多细节,例如异常处理、多参数传递等。只是为了展示事件订阅/发布模式。
很明显的看出,我们使用这种设计模式对异步编程做了逻辑上的分离,将其语义化为
// 一些事件可能会被触发eventEmitter.on// 当它发生的时候,要这样处理eventEmitter.emit
也就是说,我们将最初的 Callback 变成了事件监听器,从而优雅地解决异步编程。
Promise <= 事件发布/订阅模式
使用事件发布/订阅模式时,需要我们事先严谨地设置目标,也就是上面所说的,必须要缜密地设定好有哪些事件会发生。这与我们语言的线性思维很违和。那么有没有一种方式可以解决这个问题,社区产出了 Promise。
const promise = new Promise(function(resolve, reject) {
try {
setTimeout(() => {
resolve('hello world')
}, 500)
} catch (error) {
reject(error)
}
})
// 语义就变为先发生一些异步行为,then 我们应该这么处理promise.then(msg => console.log(msg)).catch(error => console.log('err', error))
新闻热点
疑难解答
图片精选