分別對兩種鉤子做異步討論:
- 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也可實現類似功能