前言
异步编程从早期的 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))
那么这种 Promise 与事件发布\订阅模式有什么联系呢?我们可以利用 EventEmitter 来实现 Promise,这样可能会对你有所启发。
我们可以将 Promise 视为一个 EventEmitter,它包含了 { state: 'pending' } 来描述当前的状态,同时侦听它的变化
- 当成功时 { state: 'fulfilled' },要做些什么 on('resolve', callback);
- 当失败时 { state: 'rejected' },要做些什么 on('reject', callback)。
具体实现如下
const { EventEmitter } = require('events') class usrPromise extends EventEmitter { // 构造时候执行 constructor(executor) { super() // 发布 const resolve = (value) => this.emit('resolve', value) const reject = (reason) => this.emit('reject', reason) if (executor) { // 模拟 event loop,注此处利用 Macrotask 来模拟 Microtask setTimeout(() => executor(resolve, reject)) } } then(resolveHandler, rejectHandler) { const nextPromise = new usrPromise() // 订阅 resolve 事件 if (resolveHandler) { const resolve = (data) => { const result = resolveHandler(data) nextPromise.emit('resolve', result) } this.on('resolve', resolve) } // 订阅 reject 事件 if (rejectHandler) { const reject = (data) => { const result = rejectHandler(data) nextPromise.emit('reject', result) } this.on('reject', reject) } else { this.on('reject', (data) => { promise.emit('reject', data) }) } return nextPromise } catch(handler) { this.on('reject', handler) } }
我们使用 then 方法来将预先需要定义的事件侦听器存放起来,同时在 executor 中设定这些事件该在什么时候实行。
可以看出从事件发布\订阅模式到 Promise,带来了语义上的巨大变革,但是还是需要使用 new Promise 来描述整个状态的转换,那么有没有更好地实现方式呢?
async、await <= Promise、Generator
async、await 标准是 ES 2017 引入,提供一种更加简洁的异步解决方案。
async function say(greeting) { return new Promise(function(resolve, then) { setTimeout(function() { resolve(greeting) }, 1500) }) } ;(async function() { let v1 = await say('Hello') console.log(v1) let v2 = await say('World') console.log(v2) })()
await 可以理解为暂停当前 async function 的执行,等待 Promise 处理完成。。若 Promise 正常处理(fulfilled),其回调的resolve函数参数作为 await 表达式的值。
async、await 的出现,减少了多个 then 的链式调用形式的代码。下面我们结合 Promise 与 Generator 来实现 async、await
function async(makeGenerator) { return function() { const generator = makeGenerator.apply(this, arguments) function handle({ value, done }) { if (done === true) return Promise.resolve(value) return Promise.resolve(value).then( (res) => { return handle(generator.next(res)) }, function(err) { return handle(generator.throw(err)) } ) } try { return handle(generator.next()) } catch (ex) { return Promise.reject(ex) } } } async(function*() { var v1 = yield say('hello') console.log(1, v1) var v2 = yield say('world') console.log(2, v2) })()
本质上就是利用递归完成 function* () { ... } 的自动执行。相比与 Generator 函数,这种形式无需手动执行,并且具有更好的语义。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
RTX 5090要首发 性能要翻倍!三星展示GDDR7显存
三星在GTC上展示了专为下一代游戏GPU设计的GDDR7内存。
首次推出的GDDR7内存模块密度为16GB,每个模块容量为2GB。其速度预设为32 Gbps(PAM3),但也可以降至28 Gbps,以提高产量和初始阶段的整体性能和成本效益。
据三星表示,GDDR7内存的能效将提高20%,同时工作电压仅为1.1V,低于标准的1.2V。通过采用更新的封装材料和优化的电路设计,使得在高速运行时的发热量降低,GDDR7的热阻比GDDR6降低了70%。
更新日志
- 2019明达发烧碟MasterSuperiorAudiophile[WAV+CUE]
- 蔡幸娟.1993-相爱容易相处难【飞碟】【WAV+CUE】
- 陆虎.2024-是否愿意成为我的全世界【Hikoon】【FLAC分轨】
- 关淑怡.2009-ERA【星娱乐】【WAV+CUE】
- 林忆莲《关于她的爱情故事》2022新世纪MQA 24K金碟限量版[WAV+CUE]
- 张雨生1993《一天到晚游泳的鱼》台湾G字首版[WAV+CUE][1G]
- 群星《试音五大女声》[WAV+CUE][1G]
- 魔兽世界wlk武器战一键输出宏是什么 wlk武器战一键输出宏介绍
- 魔兽世界wlk狂暴战一键输出宏是什么 wlk狂暴战一键输出宏介绍
- 魔兽世界wlk恶魔术士一键输出宏是什么 wlk恶魔术士一键输出宏介绍
- 医学爱好者狂喜:UP主把医学史做成了格斗游戏!
- PS5 Pro评分解禁!准备升级入手吗?
- 我们盘点了近期火热的国产单机游戏!《琉隐神渡》等 你期待哪款?
- 2019年第12届广州影音展双碟纪念版ADMS2CD[MP3/WAV]
- 黄安《救姻缘》台首版[WAV+CUE]