一、事件分发机制
事件分发机制是观察者模式的一种实现,常见于UI交互,给界面组件注册监听器,由界面组件发出事件,监听器接收到事件后作出响应。
事件分发机制也适用于异步管理,其实UI的事件分发也是一种异步通讯,理解Promise实现之前应该对事件分发器(Event Dispatcher)的实现有所了解,DOM对象也可看作是一种分发器,addEventListener
跟Promise.then
类似,都是监听器注册的入口。
二、分发器雏形
实现一个简单的事件分发器Dispatcher
,用于封装异步任务task
,分发器构造函数须接收一个任务函数task()
,对外暴露两个接口,注册监听器register()
和执行任务execute()
,私有方法trigger()
用于触发事件。
接口 | 参数 | 说明 |
---|---|---|
register | success,error | 任务执行成功回调success() ,异常回调error() |
trigger | type,data | type 事件类型,data附加参数如含异常信息等 |
execute | 执行任务函数task() |
function Dispatcher(task)
{
if(typeof task !=="function")
{
throw Error("task is not a function.");
}
this.task=task;
this.listeners=[];
};
Dispatcher.prototype.register=function(on_success=()=>{},on_error=()=>{}){
this.listeners.push({
on_success,
on_error
});
};
Dispatcher.prototype.trigger=function(type,data)
{
switch(type)
{
case "success":
{
this.listeners.forEach((listener)=>
{
listener.on_success(data);
});
}
break;
case "error":
{
this.listeners.forEach((listener)=>
{
listener.on_error(data);
});
}
break;
}
};
Dispatcher.prototype.execute=function(){
const trigger_success=(data)=>
{
this.trigger("success",data);
};
const trigger_error=(data)=>
{
this.trigger("error",data);
};
this.task(trigger_success,trigger_error);
};
task()
被执行时传入了两个函数trigger_success()
和trigger_error()
,都是对trigger()
接口的封装,用于任务结束时反馈执行状态是成功success
还是异常error
。
例2-1 分发器的基础应用:
let task=new Dispatcher((trigger_success,trigger_error)=>
{
trigger_success();
});
task.register(()=>
{
console.log("task executed.");
});
task.execute(); // 启动事件源
输出:
task executed.
三、挖坑,实现响应延迟注册
例3-1:
let task=new Dispatcher((trigger_success,trigger_error)=>
{
trigger_success();
});
task.register(()=>
{
console.log("task executed.");
});
task.execute(); // 启动事件源
setTimeout(()=>
{
// 任务结束后注册监听器
task.register(()=>
{
console.log("after 3s."); // 不会被响应
});
},3000);
输出:
task executed.
如上例子,观察者模式实现的典型是先注册后执行,对于要等到任务结束后才注册监听器的场景遇到的不多,这里挖个坑去实现它。
首先明确,任务的执行结果是不变的(must not transition to any other state),用Promise/A+的话就是说,任务状态分三种,任务状态初始状态是"pending"
,完成态是"fulfilled"
,拒绝态是"rejected"
,状态仅能由初始态转为完成态或拒绝态。
这里定义分发器任务的三个状态为初始态"pending"
,成功态"success"
和异常态"error"
。
记得注册接口声明为register(on_success,on_error)
,接口的两个传入函数分别对应成功态和异常态的响应,由分发器负责回调。
任务是否执行成功,由task()
实现逻辑确定,执行成功,回调一下trigger_success()
进行反馈,分发器自动触发各个监听器。任务结束后注册的监听器,由于脱离了运作流程是不可能得到响应的,“响应延迟注册”换种说法应该是询问,即询问执行结果,只不过询问的接口依然是register()
,两种职责集中在一个接口,是不是有违反单一职责原则的嫌疑呢。
事实上,register()
接口会发生职责转换,从注册监听器用途转为结果查询用途,任务函数task()
尚未执行,此时register()
为注册监听器用途,一切按流程走;trigger_success
/trigger_error
被回调后,预注册的监听器得到反馈,之后register()
就转为查询用途。
注册功能原有运作机制已经具有,添加查询用途需要稍作改动,设立标记变量state
用于状态的维护和获取,初始化为pending
,还有就是缓存任务执行结果(Result)。
function Dispatcher(task)
{
if(typeof task !=="function")
{
throw Error("task is not a function.");
}
this.state="pending"; // 维护状态
this.result=undefined; // 缓存task执行结果
this.task=task;
this.listeners=[];
};
任务函数task()
回调trigger_success()
或trigger_error()
事实上就是进行状态转换。
Dispatcher.prototype.trigger=function(type,data){
this.state=type; // 修改状态
this.result=data; //缓存结果
switch(type)
{
case "success":
{
this.listeners.forEach((listener)=>
{
listener.on_success(data);
});
}
break;
case "error":
{
this.listeners.forEach((listener)=>
{
listener.on_error(data);
});
}
break;
}
};
当状态不为pending
时,可以认为任务已经执行结束,register()
接口职责转为结果查询,根据状态反馈结果即可,监听器不需要缓存进listeners[]
。
Dispatcher.prototype.register=function(on_success=()=>{},on_error=()=>{}){
switch(this.state)
{
case "pending":
{
this.listeners.push({
on_success,
on_error
});
}
break;
case "success":
{
on_success(this.result);
}
break;
case "error":
{
on_error(this.result);
}
break;
}
};
改动后再执行例3-1:
输出:
task executed.
after 3s.
四、再挖坑,实现链式调用
链式调用最早出现于jQuery:
$(this).children("a").siblings().removeClass("active").addClass("active");
jQuery链式调用返回this
,由于操作对象都是自身,操作结果最终会被复合在一起。但对于事件分发器来说,任务逻辑基本是独立的,链式调用的下一个对象应该是一个新的分发器对象,返回分发器自身会造成自身状态反复改写,违反状态转换原则。
打个草稿,让register()
返回一个新的分发器对象,实现任务串联,大概就是如下样子:
let task1=new Dispatcher((trigger_success,trigger_error)=>
{
trigger_success(); // 确认初始状态,启动链式操作
});
task1.register(()=>
{
... // task1 handler
}).register(()=>
{
... // task2 handler
}).register(()=>
{
... // task3 handler
});
注意,触发链式操作首先要对链头任务进行反馈。
当监听器回调函数on_success()
或on_error()
有返回值,并且返回值形似一个分发器对象(thenable),register()
就接受该对象作为返回值。如果监听器没有返回值,那么register()
就返回一个默认实现的分发器,并且该分发器的状态继承本分发器的状态(adopt its state)。
register()
职责是注册监听器时,由于监听器尚未作出响应自然就无法取得on_success()
/on_error()
的返回值,此时接口返回的是默认值,后续被注册的观察对象也是该默认值。on_success()
/on_error()
要被延迟到接口trigger()
被启动时回调,由于trigger()
接口自身没有定义返回值,回调返回的自定义分发器根本没有机会对外暴露接口,为了将返回的默认值与on_success()
/on_error()
的自定义分发器内容关联起来,应该将默认的返回值与监听器关联起来一并缓存(deferred),待监听器响应前,将自定义分发器的上下文替换掉被缓存默认值分发器的上下文。
当register()
职责是查询时,如果监听器返回值具有分发器特征,那么就认为该返回值是一个Dispatcher
对象,直接启动即可。
当任务执行异常时,若register()
注册的监听器没有实现异常处理on_error()
,则将本级的异常状态和数据往下一级分发器传递,直到被接收为止。异常被接收后,为了使执行链能继续执行,若下一级分发器没有自定义实现,则修改下一级分发器状态为"success"
继续执行,直到执行链执行完毕。
// 默认的监听器回调实现,用于确保register有返回值,on_success/on_error没有显式被实现可以变相理解为采用了默认实现
// 回调函数返回一个默认实现的分发器,分发器的任务实现就是继承状态和结果(adopt its state),是状态传递的基础
Dispatcher.prototype.on_success=function(data){
return new Dispatcher(function(trigger_success,trigger_error){
trigger_success(data); //继承状态 ‘success’和结果result,两者应当同时继承
});
};
// 见上,默认的on_error实现
Dispatcher.prototype.on_error=function(data){
return new Dispatcher(function(trigger_success,trigger_error){
trigger_error(data);
});
};
// 回调函数给与了默认指定,确保注册的同时返回新的分发器
// 自定义任务内容在需要在回调中实现并返回
Dispatcher.prototype.register=function(on_success=Dispatcher.prototype.on_success,on_error=Dispatcher.prototype.on_error){
let next; // 下一个分发器
const noop=(trigger_success,trigger_error)=> // 默认的空任务实现
{
switch(this.state) // 本级状态,注意指针,该函数修改下一个分发器的状态
{
case "success":
{
if(on_success===Dispatcher.prototype.on_success) // 是否实现了回调
{
trigger_success(this.result); // 继承上一级状态数据,下一级state修改为success
}
else
{
trigger_success(); // 已实现,回调下一级on_success
}
}
break;
case "error":
{
if(on_error===Dispatcher.prototype.on_error) // 是否实现了回调
{
trigger_error(this.result); // 没有实现就继续路由
}
else
{
trigger_success(); // 已实现,可认为异常已处理,回调下一级on_success
}
}
}
};
switch(this.state)
{
case "pending":
{
next=new Dispatcher(noop); // 默认分发器
this.listeners.push({
on_success,
on_error,
dispatcher:next // 缓存默认值
});
}
break;
case "success":
{
if(typeof on_success==="function")
{
next=on_success(this.result); // 产生下一级分发器
if(!next|| typeof next.register!=="function") // 返回结果是否具有分发器特征
{
next=new Dispatcher(noop);
}
next.execute();
}
}
break;
case "error":
{
if(typeof on_error==="function")
{
next=on_error(this.result);
if(!next|| typeof next.register!=="function") // 同上,检查是否有自定义任务的分发器
{
next=new Dispatcher(noop);
}
next.execute();
}
}
break;
}
return next;
};
Dispatcher.prototype.trigger=function(type,data){
function switchContext(cache,next)
{
// 返回值是否具有分发器特征
if(typeof next.register==="function")
{
cache.task=next.task; // 替换上下文
next.task=null;
}
}
this.state=type;
this.result=data;
switch(type)
{
case "success":
{
this.listeners.forEach((listener)=>{
if(typeof listener.on_success==="function")
{
let implement=listener.on_success(data); // 接受自定义实现
let deferred=listener.dispatcher;
if(implement&& typeof implement.register==="function")
{
switchContext(deferred,implement); // 替换上下文
}
deferred.execute(); // 默认执行noop
}
});
}
break;
case "error":
{
this.listeners.forEach((listener)=>{
if(typeof listener.on_error==="function")
{
let implement=listener.on_error(data);
let deferred=listener.dispatcher;
if(implement&& typeof implement.register==="function")
{
switchContext(deferred,implement);
}
deferred.execute(); // 默认执行noop
}
});
}
break;
}
};
例4-1 状态继承:
let task=new Dispatcher((trigger_success)=>{
trigger_success(100);
});
task.register().register().register().register((data)=>{
console.log(data);
});
task.execute();
输出:
100
例4-2 链式操作:
let task1=new Dispatcher((trigger_success,trigger_error)=>
{
trigger_success();
});
// register 注册状态
task1.register(()=>
{
console.log("task1 executed.");
return new Dispatcher((trigger_success,trigger_error)=>
{
setTimeout(()=>
{
trigger_success();
},2000)
});
}).register(()=>
{
console.log("task2 executed after 2s.");
});
task1.execute(); // 启动任务后,register注册态转查询态
// register 查询状态
task1.register(()=>
{
return new Dispatcher((trigger_success,trigger_error)=>
{
trigger_success();
});
}).register(()=>
{
console.log("task3 executed.");
});
输出:
task1 executed.
task3 executed.
task2 executed after 2s.
五、catch()异常捕获
catch()
可以看作register()
的变形,用于捕获任务异常。
Dispatcher.prototype.catch=function(on_catch){
return this.register(null,on_catch);
};
稍作修改也可以用于捕获运行时异常。
Dispatcher.prototype.execute=function(){
const trigger_success=(data)=>{
this.trigger("success",data);
};
const trigger_error=(data)=>{
this.trigger("error",data);
};
try
{
this.task(trigger_success,trigger_error);
}
catch(e)
{
trigger_error(e);
}
};
五、“去掉”execute()接口
由于已经实现了延迟注册响应,execute()
已经没有显式调用的意义了,反正监听器什么时候注册都会得到响应,分发器在构造的时候顺便就把任务启动得了。
现在把execute()
接口改为私有(private),只在内部调用,由于私有、公有(public)的概念对JavaScript意义不大,这里就当看不见好了。
function Dispatcher(task)
{
if(typeof task!=="function")
{
throw Error("task is not a function.");
}
this.state="pending"; // 维护状态
this.result=undefined; // 缓存task执行结果
this.task=task;
this.listeners=[];
this.execute(); // *** 启动任务 ***
};
register()
接口处于什么职责状态由任务执行时长确定,任务等待外部资源(如AJAX)的时间足够长,所有监听器都会被缓存。
接口register()
不需要显式调用execute()
了。
// 回调函数给与了默认指定,确保注册的同时返回新的分发器
// 自定义任务内容在需要在回调中实现并返回
Dispatcher.prototype.register=function(on_success=Dispatcher.prototype.on_success,on_error=Dispatcher.prototype.on_error){
let next; // 下一个分发器
const noop=(trigger_success,trigger_error)=> // 默认的空任务实现
{
switch(this.state) // 本级状态,注意指针,该函数修改下一个分发器的状态
{
case "success":
{
if(on_success===Dispatcher.prototype.on_success) // 是否实现了回调
{
trigger_success(this.result); // 继承上一级状态数据,下一级state修改为success
}
else
{
trigger_success(); // 已实现,回调下一级on_success
}
}
break;
case "error":
{
if(on_error===Dispatcher.prototype.on_error) // 是否实现了回调
{
trigger_error(this.result); // 没有实现就继续路由
}
else
{
trigger_success(); // 已实现,可认为异常已处理,回调下一级on_success
}
}
}
};
switch(this.state)
{
case "pending":
{
next=new Dispatcher(noop); // 默认分发器
this.listeners.push({
on_success,
on_error,
dispatcher:next // 缓存默认值
});
}
break;
case "success":
{
if(typeof on_success==="function")
{
next=on_success(this.result); // 产生下一级分发器
if(!next|| typeof next.register!=="function") // 返回结果是否具有分发器特征
{
next=new Dispatcher(noop);
}
}
}
break;
case "error":
{
if(typeof on_error==="function")
{
next=on_error(this.result);
if(!next|| typeof next.register!=="function") // 返回结果是否具有分发器特征
{
next=new Dispatcher(noop);
}
}
}
break;
}
return next;
};
由于分发器构造的时候就立即开始任务,被缓存的下一级分发器已经执行完task
而上下文都还未切换,被缓存的分发器执行的是“空任务”,不会改变状态,监听器是不会被触发的,因此trigger()
接口内还需要保留显式调用execute()
。
去掉execute()
显式调用后,再执行一下例4-2,结果是一样的。
let task1=new Dispatcher((trigger_success,trigger_error)=>
{
trigger_success();
});
task1.register(()=>
{
console.log("task1 executed.");
return new Dispatcher((trigger_success,trigger_error)=>
{
setTimeout(()=>
{
trigger_success();
},2000)
});
}).register(()=>
{
console.log("task2 executed after 2s.");
});
task1.register(()=>
{
return new Dispatcher((trigger_success,trigger_error)=>
{
trigger_success();
});
}).register(()=>
{
console.log("task3 executed.");
});
输出:
task1 executed.
task3 executed.
task2 executed after 2s.
Dispatcher.js
function Dispatcher(task)
{
if(typeof task!=="function")
{
throw Error("task is not a function.");
}
this.state="pending"; // 维护状态
this.result=undefined; // 缓存task执行结果
this.task=task;
this.listeners=[];
this.execute();
};
// 默认的监听器回调实现,用于确保register有返回值,on_success/on_error没有显式被实现可以变相理解为采用了默认实现
// 回调函数返回一个默认实现的分发器,分发器的任务实现就是继承状态和结果(adopt its state),是状态传递的基础
Dispatcher.prototype.on_success=function(data){
return new Dispatcher(function(trigger_success,trigger_error){
trigger_success(data); //继承状态 ‘success’和结果,两者应当同时继承
});
};
// 见上,默认的on_error实现
Dispatcher.prototype.on_error=function(data){
return new Dispatcher(function(trigger_success,trigger_error){
trigger_error(data);
});
};
Dispatcher.prototype.catch=function(on_catch){
return this.register(null,on_catch);
};
// 回调函数给与了默认指定,确保注册的同时返回新的分发器
// 自定义任务内容在需要在回调中实现并返回
Dispatcher.prototype.register=function(on_success=Dispatcher.prototype.on_success,on_error=Dispatcher.prototype.on_error){
let next; // 下一个分发器
const noop=(trigger_success,trigger_error)=> // 默认的空任务实现
{
switch(this.state) // 本级状态,注意指针,该函数修改下一个分发器的状态
{
case "success":
{
if(on_success===Dispatcher.prototype.on_success) // 是否实现了回调
{
trigger_success(this.result); // 继承上一级状态数据,下一级state修改为success
}
else
{
trigger_success(); // 已实现,回调下一级on_success
}
}
break;
case "error":
{
if(on_error===Dispatcher.prototype.on_error) // 是否实现了回调
{
trigger_error(this.result); // 没有实现就继续路由
}
else
{
trigger_success(); // 已实现,可认为异常已处理,回调下一级on_success
}
}
}
};
switch(this.state)
{
case "pending":
{
next=new Dispatcher(noop); // 默认分发器
this.listeners.push({
on_success,
on_error,
dispatcher:next // 缓存默认值
});
}
break;
case "success":
{
if(typeof on_success==="function")
{
next=on_success(this.result); // 产生下一级分发器
if(!next|| typeof next.register!=="function") // 返回结果是否具有分发器特征
{
next=new Dispatcher(noop);
}
}
}
break;
case "error":
{
if(typeof on_error==="function")
{
next=on_error(this.result);
if(!next|| typeof next.register!=="function") // 返回结果是否具有分发器特征
{
next=new Dispatcher(noop);
}
}
}
break;
}
return next;
};
Dispatcher.prototype.trigger=function(type,data){
function switchContext(cache,next)
{
// 返回值是否具有分发器特征
if(typeof next.register==="function")
{
cache.task=next.task; // 替换上下文
next.task=null;
}
}
this.state=type;
this.result=data;
switch(type)
{
case "success":
{
this.listeners.forEach((listener)=>{
if(typeof listener.on_success==="function")
{
let implement=listener.on_success(data); // 接受自定义实现
let deferred=listener.dispatcher;
if(implement&& typeof implement.register==="function")
{
switchContext(deferred,implement); // 替换上下文
}
deferred.execute(); // 执行自定义的task
}
});
}
break;
case "error":
{
this.listeners.forEach((listener)=>{
if(typeof listener.on_error==="function")
{
let implement=listener.on_error(data);
let deferred=listener.dispatcher;
if(implement&& typeof implement.register==="function")
{
switchContext(deferred,implement);
}
deferred.execute();
}
});
}
break;
}
};
Dispatcher.prototype.execute=function(){
const trigger_success=(data)=>{
this.trigger("success",data);
};
const trigger_error=(data)=>{
this.trigger("error",data);
};
try
{
this.task(trigger_success,trigger_error);
}
catch(e)
{
trigger_error(e);
}
};
六、Promise
Promise原理与事件分发同源,但语义与事件分发、观察者模式等差太远,不容易直接理解。
Dispatcher.register
类似Promise.then
,Promise.then
延迟注册响应的特点会让人产生Promise
对象可以并行执行的错觉。
打开文本编辑器,对Dispatcher
代码进行一些文本替换,就是一个简陋的Promise
。
Promise.js
function Promise(task)
{
if(typeof task!=="function")
{
throw Error("task is not a function.");
}
this.state="pending";
this.result=undefined;
this.task=task;
this.listeners=[];
this.execute();
};
Promise.prototype.on_resolve=function(data){
return new Promise(function(resolve,reject){
resolve(data);
});
};
Promise.prototype.on_reject=function(data){
return new Promise(function(resolve,reject){
reject(data);
});
};
Promise.prototype.catch=function(on_catch){
return this.then(null,on_catch);
};
Promise.prototype.then=function(on_resolve=Promise.prototype.on_resolve,on_reject=Promise.prototype.on_reject){
let next;
const noop=(resolve,reject)=>
{
switch(this.state)
{
case "resolved":
{
if(on_resolve===Promise.prototype.on_resolve)
{
resolve(this.result);
}
else
{
resolve();
}
}
break;
case "rejected":
{
if(on_reject===Promise.prototype.on_reject)
{
reject(this.result);
}
else
{
resolve();
}
}
}
};
switch(this.state)
{
case "pending":
{
next=new Promise(noop);
this.listeners.push({
on_resolve,
on_reject,
promise:next
});
}
break;
case "resolved":
{
if(typeof on_resolve==="function")
{
next=on_resolve(this.result);
if(!next|| typeof next.then!=="function")
{
next=new Promise(noop);
}
}
}
break;
case "rejected":
{
if(typeof on_reject==="function")
{
next=on_reject(this.result);
if(!next|| typeof next.then!=="function")
{
next=new Promise(noop);
}
}
}
break;
}
return next;
};
Promise.prototype.trigger=function(type,data){
function switchContext(cache,next)
{
if(typeof next.then==="function")
{
cache.task=next.task;
next.task=null;
}
}
this.state=type;
this.result=data;
switch(type)
{
case "resolved":
{
this.listeners.forEach((listener)=>{
if(typeof listener.on_resolve==="function")
{
let implement=listener.on_resolve(data);
let deferred=listener.promise;
if(implement&& typeof implement.then==="function")
{
switchContext(deferred,implement);
}
deferred.execute();
}
});
}
break;
case "rejected":
{
this.listeners.forEach((listener)=>{
if(typeof listener.on_reject==="function")
{
let implement=listener.on_reject(data);
let deferred=listener.promise;
if(implement&& typeof implement.then==="function")
{
switchContext(deferred,implement);
}
deferred.execute();
}
});
}
break;
}
};
Promise.prototype.execute=function(){
const resolve=(data)=>{
this.trigger("resolved",data);
};
const reject=(data)=>{
this.trigger("rejected",data);
};
try
{
this.task(resolve,reject);
}
catch(e)
{
reject(e);
}
};
例6-1:
new Promise(function(resolve,reject){
console.log('p1 resolved.');
resolve(100);
}).then(function(){
return new Promise(function(resolve,reject){
setTimeout(function(){
console.log('p2 rejected.');
reject(200);
},2000);
});
}).then(function(){
return new Promise(function(resolve,reject){
console.log('p3 resolved.');
resolve(300);
});
}).then().then().then(function(data){
console.log('data: '+data+" after 2s.");
}).catch().catch().catch(function(error){
console.log('error: '+error+" after 2s.");
}).then(function(){
console.log(500);
},function(){
console.log(400);
});
输出:
p1 resolved.
p2 rejected.
error: 200 after 2s.
500