过滤器异步解决方案
更新于:2023年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 也可实现类似功能