分别对两种钩子做异步讨论:
- Action
对于动作而言,它只是在正常工作流中被同步触发的行为,并不会对原工作流产生影响。因此异步情况【不考虑】。 - Filter
对于过滤器而言,它需要返回数据后正常工作流才能根据返回的数据继续工作,如果它出现异步,工作流也需要等待它完成后才能继续执行。因此它的异步情况【应该被考虑】。
普通数据过滤
正常运行的工作流中执行了一个名为”sample_filter”的过滤器,被过滤的参数是“filterMe”字符串,以及其他两个辅助参数:
var result = apply_filters("sample_filter","filterMe","otherArg1","otherArg2");
console.log(result);
//...后续其他操作
在未添加”sample_filter”过滤器前,运行结果屏幕显示字符串:filterMe
现在添加一个过滤器,目的是将被过滤的字符串和其他两个参数使用~符号串接起来:
//过滤器
function sample_filter_connect_strings( original_string, arg1, arg2 ) {
return original_string+"~"+arg1+"~"+arg2;
}
add_filter("sample_filter", sample_filter_connect_strings);
//正常工作流
var result = apply_filters("sample_filter","filterMe","otherArg1","otherArg2");
console.log(result);
//...后续其他操作
此时,运行结果屏幕显示过滤后的字符串:filterMe~otherArg1~otherArg2
异步过滤解决方案
同样是上面的例子,如果过滤的字符串是来自后端服务器或需要一定时间后才能返回,那么怎么办呢?假设现在过滤器本身需要延迟1秒后才返回结果,我们首先会想到的是:
//过滤器
function sample_filter_connect_strings( original_string, arg1, arg2 ) {
setTimeout(function(){
return original_string+"~"+arg1+"~"+arg2;
},1000)
}
add_filter("sample_filter", sample_filter_connect_strings);
//正常工作流
var result = apply_filters("sample_filter","filterMe","otherArg1","otherArg2");
console.log(result);
//...后续其他操作
我们逻辑上认为延迟后返回数据即可。然而事实上屏幕却显示:undefined
原因是等待时间造成了异步行为,浏览器并不会停在那里不动。也就是说,在1s延时的时候,浏览器仍然在继续执行代码。由于过滤器并没有return,因此得到的结果是undefined
这种行为新手很容易犯错:这类似于发起的Ajax请求,浏览器会在某个时间后才会得到结果,然而后面的代码段并不会暂停等待。通常这种情况我们的处理方式是callback回调。
提供一个我个人提出的解决方案
我们这样理解:原始的工作流“被过滤的数据”是一个function,这个function本身接受一个回调参数。在数据处理完成后,将数据回调给参数继续后续处理。因此我们将工作流改造为下面的样子来进行后续处理:
var tobe_Filtered = function(resolve) {
resolve( "filterMe" );
}
var callback = function(result) {
console.log(result);
//...后续其他操作
}
var result_function = apply_filters("sample_filter",tobe_Filtered,"otherArg1","otherArg2");
result_function(callback)
运行上述代码时,第8行先尝试过滤 tobe_Filtered 方法,发现未添加过滤器,因此得到的结果仍然是 tobe_Filtered。第9行执行了result_function(tobe_Filtered)方法。执行时,调用了第2行的 resolve,而 resolve 又是在第9行传入的callback,因此,callback 接收到了第 1行返回的“filterMe”数据。
简化一下会更好看:
apply_filters("sample_filter",function( resolve ){
resolve( "filterMe" );
},"otherArg1","otherArg2")(function( result ){
console.log(result);
//...后续其他操作
})
根据“过滤后的数据和过滤前的数据类型原则上相同”这个条件,因此过滤器也应该返回的是function。
由于上一步返回的是方法,要得到数据需要等待上一步回调,因此需要编写上一步的回调函数。
//过滤器
function sample_filter_connect_strings( tobe_Filtered_function, arg1, arg2 ) {
return function( resolve ) {
//从被过滤的方法中回调数据
tobe_Filtered_function(function( data_tobe_filtered ){
//异步回调数据
setTimeout(function(){
resolve( data_tobe_filtered+"~"+arg1+"~"+arg2 );
},1000)
})
}
}
add_filter("sample_filter", sample_filter_connect_strings);
//正常工作流
apply_filters("sample_filter",function( resolve ){
resolve( "filterMe" );
},"otherArg1","otherArg2")(function( result ){
console.log( result );
//...后续其他操作
})
于是,就实现了异步过滤的效果。
如果只是为了得到新数据,而不是从上一步的结果中过滤,也可以忽略被过滤的方法,直接resolve新数据:
//过滤器
function sample_filter_connect_strings( tobe_Filtered_function, arg1, arg2 ) {
return function( resolve ) {
//直接异步resolve新数据,而不从上一步的回调方法中得到数据
setTimeout(function(){
resolve( "New Data" );
},1000)
}
}
add_filter("sample_filter", sample_filter_connect_strings);
//正常工作流
apply_filters("sample_filter",function( resolve ){
resolve( "filterMe" );
},"otherArg1","otherArg2")(function( result ){
console.log( result );
//...后续其他操作
})
总结
异步过滤解决方案过滤的并不是数据,而是方法。该方法接受一个回调函数,将数据回调给函数后进行后续处理。
由于上一步返回的是方法,要得到数据需要等待上一步回调,因此需要编写上一步的回调函数。
使用Promise也可实现类似功能