Npcink Blog
切换菜单

过滤器异步解决方案

更新于:2023年4月4日

本页目录4 节

分别对两种钩子做异步讨论:

  • 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 也可实现类似功能