EventEmitter 和 Promise

273 阅读3分钟

假设有这样的函数

<body>
    <button id="x">点击</button>
    <script>
    	const button = document.getElementById('x')
			const fn = () => console.log('hi')
		</script>
</body>

如何在不点击 button 的情况下触发 fn 函数(用 js 触发函数

只需要在 script 加上这些代码

const button = document.getElementById('x')
const fn = () => console.log('hi')						//声明事件处理函数
button.addEventListener('click', fn)					//监听 click 事件
//button.removeEventListener('click', fn)			//取消监听

setTimeout(() => {
    //派发 触发 发射 click 事件
    button.dispatchEvent(new Event('click'))	
}, 5000);

如何添加上事件名

class EventEmitter {
    constructor() {
        this.queue = { }             //构造一个数组 queue
    }

    addEventListener(name, fn) {
        this.queue[name] = this.queue[name] || []       //如果有this.queue[name]则赋值,如果没有则等于空
        this.queue[name].push(fn)         //往数组里面添加函数
    }
    removeEventListener(name, fn) {
        if(this.queue[name] === undefined) { return }   //防御性
        const index = this.queue[name].indexOf(fn)    
        if(index < 0) return
        this.queue[name].splice(index, 1) //删掉数组里的函数
    }
    dispatchEvent(name, data) {
        this.queue[name]?.forEach(fn => fn(data))      //判断this.queue[name]是否存在,如果是则遍历数组,把数组里的函数全部执行一遍,不是则什么都不做
    }
}

const obj = new EventEmitter()
const fn = (data) => console.log(data)          //声明函数,

obj.addEventListener('kenny', fn)                    //添加函数

setTimeout(() => {
    obj.dispatchEvent('kenny', '你好')                     //四秒之后触发函数
}, 4000);

如何添加参数?

  	dispatchEvent(name, ...args) {
        this.queue[name]?.forEach(fn => fn.call(undefined, ...args))      //判断this.queue[name]是否存在,如果是则遍历数组,把数组里的函数全部执行一遍,不是则什么都不做
    }

发布订阅总结

发布订阅的特点?

  • api 提供了 addEventListener / on / subscribe
  • api 提供了 dispatchEvent / emit / trigger
  • api 提供了 removeEventListener / off / unsubscribe
  • 满足上述条件的对象被称为 EventEmitter,实现了发布订阅模式
  • 把回调(函数)放在队列(数组)里,等待被逐个调用

发布订阅解决了什么问题?

  • 所有异步任务都可以用发布订阅来管理
  • 先订阅成功事件和失败事件
    • xhr.on('load'); xhr.on('error')
  • 再在任务完成时触发成功或失败事件
    • xhr.emit('load'); xhr.emit('error')
  • 这就是一个通用的异步任务管理方案

缺点是什么?

  • 事件少时还行;但当事件过多时,很难管理
this.queue[name]?.forEach(fn => fn(data))						//核心代码

promise

promise 对象用于表示一个异步操作的最终完成(或失败)及其结果值,代码示例如下

function ysz() {
    return new Promise((resolve, reject) => {				//返回一个 promise,
        setTimeout(() => {
            const n = Math.floor(Math.random() * 6) + 1
            resolve(n)										//成功时调用resolve(data),失败时调用reject(reason)
        }, 3000);
    })
}
/* resolve 会去调用 success,reject 会去调用 fail
ysz().then(success, fail)								//用then方法返回一个 promise,传入 promise 的成功和失败情况的回调函数作为参数

function success(data){									//声明成功函数,传入 data 
    console.log('成功, 点数为' + data)
}
function fail(reason){									//声明失败函数,传入 reason 作为失败原因
    console.log('失败了,原因是' + reason);

Promise 的三种状态

  • pending 待定,即初始状态,既没有被兑现,也没有被拒绝
  • fulfilled 解决,操作成功完成
  • rejected 拒绝,操作失败

状态变化只有三种情况:待定 => 解决、待定 => 拒绝、待定 => 待定,没有其它

Promise 的链式掉用

即一个 promise 之后又是一个 promise,因为then() 方法返回一个 promise,因此可以将进一步的操作与一个变为已敲定状态的 promise 关联起来

.then(success, fail).then(success2, fail2)

ysz().then((n) => {console.log(n>3?'大':'小'); return n},				//如果成功,则
           (reason) => {console.log('失败了,因为' + reason);throw reason}
    ).then((data) => {console.log(data)},
            (reason) => {console.log('失败了,原因是' + reason);})