2016-09-19 49 views
0

我有一種情況,至今我一直無法找到滿意的解決方案。以下是高層次的代碼。JavaScript內部循環中的延遲時間爲

var a = [1, 2, 3, 4, 5, 6, 7, 8, 9], 
    o = {a:1, b:2, c:3, d:10, e:11, f:12, g:7, h:8, i:9}; 

function matched(i, j) { 
    return a[i]===o[j]; 
} 

for (var i=0; i<a.length; ++i) { 
    for (var j in o) { 
    if (matched(i, j)) console.log(a[i]); 
    } 
} 

我有一個數組和一個對象。我循環訪問數組,然後是對象,通過函數matched()查找匹配項,該函數返回布爾值truefalse。如果條件是true,那麼我記錄數組項目。如果您現在運行代碼(https://jsfiddle.net/thdoan/0tubbokj/),您應該看到數字1-3和7-9輸出到控制檯。

我想要做的是在每個數字之間輸出帶有一秒延遲的數字。我知道如何在每個循環迭代之間引入延遲,但我只想爲打印的數字添加延遲(即當matched()返回true時)。

澄清:我目前的解決方案,我不滿意,是將匹配的項目保存到一個單獨的數組,並在延遲迭代該數組,但我正在尋找一種解決方案,不需要創建一個新的數組。

+0

您需要爲遞歸。使用遞歸函數更改循環for()。如果你不能,你可以'setTimeout(function(){console.log(result);},1000);'但它不好。 –

+0

@MarcosPérezGude可以在小提琴或codepen中演示您的解決方案,因爲我無法使其工作。謝謝。 – 10basetom

+0

我的建議等於TJCrowder的答案。 –

回答

3

我想要做的是與輸出中的每個數字之間的一秒鐘的延遲數。

你也評論說:

...在真正的應用程序匹配的集合可能會變得非常大,所以如果有一個我寧願不佔用更多的內存解決方案不需要輸出到第三個陣列。

爲了同時實現這些目標,你必須完全放棄你for循環,而是做一個鏈接一系列setTimeout秒。

var a = [1, 2, 3, 4, 5, 6, 7, 8, 9], 
 
    o = {a:1, b:2, c:3, d:10, e:11, f:12, g:7, h:8, i:9}; 
 

 
function matched(i, j) { 
 
    return a[i]===o[j]; 
 
} 
 

 
// Get the property names in `o`, and start at the beginning 
 
var keys = Object.keys(o); 
 
var i = 0; 
 
var keyIndex = 0; 
 
tick(); 
 

 
function tick() { 
 
    // Get the "j" value for this tick 
 
    var j = keys[keyIndex]; 
 
    
 
    // Is this a match? 
 
    var flag = matched(i, j); 
 
    if (flag) { 
 
    console.log(a[i]); 
 
    } 
 
    
 
    // Move to the next entry in our nested loops 
 
    if (++keyIndex >= keys.length) { 
 
    keyIndex = 0; 
 
    if (++i >= a.length) { 
 
     // Done 
 
     return; 
 
    } 
 
    } 
 

 
    // Continue 
 
    if (flag) { 
 
    // We output one, wait a second before next 
 
    setTimeout(tick, 1000); 
 
    } else { 
 
    // No output, continue immediately 
 
    tick(); // SEE NOTE BELOW 
 
    } 
 
}

注:如果可能有數以千計的非匹配的排成一列,你可能會考慮在tick使用一個循環,而不是鏈接到它。從ES2015開始,JavaScript應該具有尾部呼叫優化功能,我們的tick不必將自己推入堆棧(它只會回傳到開頭),但是一些JavaScript引擎尚未實現TCO,這可能會意味着你會得到一個顯着的堆棧深度。

所以,用一個循環:

var a = [1, 2, 3, 4, 5, 6, 7, 8, 9], 
 
    o = {a:1, b:2, c:3, d:10, e:11, f:12, g:7, h:8, i:9}; 
 

 
function matched(i, j) { 
 
    return a[i]===o[j]; 
 
} 
 

 
// Get the property names in `o`, and start at the beginning 
 
var keys = Object.keys(o); 
 
var i = 0; 
 
var keyIndex = 0; 
 
tick(); 
 

 
function tick() { 
 
    var match = findNextMatch(); 
 
    if (match) { 
 
    console.log(match); 
 
    setTimeout(tick, 1000); 
 
    } 
 
} 
 

 
function findNextMatch() { 
 
    var j; 
 
    var match; 
 
    
 
    while (!match && i < a.length) { 
 
    j = keys[keyIndex]; 
 
    if (matched(i, j)) { 
 
     match = a[i]; 
 
    } 
 

 
    // Move to the next entry in our nested loops 
 
    if (++keyIndex >= keys.length) { 
 
     keyIndex = 0; 
 
     ++i; 
 
    } 
 
    } 
 

 
    return match; 
 
}

其實,這似乎更清潔,我反正,即使沒有深籌碼的關注。

+1

看起來這是OP想要的。在功能上,這是最好的答案:D +1 – Cerbrus

+1

T.J Crowder我喜歡你的解決方案,它對我來說看起來很優雅。非常感謝! – 10basetom

+0

@ 10basetom:它的缺點是它必須在'o'中構建和保存這個鍵的數組,你說這可能是成千上萬的。也就是說,數組中的「數千」字符串在現代JavaScript環境中並不是什麼大問題,您可以在完成時釋放數組。另一種方法是每次在'for-in'循環中計數到現在的「j」,A)會有更高的運行時間成本(雖然不太可能足夠大以至於B)依靠穩定的'for-in'迭代順序。 *(續)* –

2

的解決方法是比較簡單的:

不要擔心,當你收集結果的記錄。相反,將全部結果存儲在新陣列中。

然後遍歷該result陣列,具有延遲:

var a = [1, 2, 3, 4, 5, 6, 7, 8, 9], 
 
    o = {a:1, b:2, c:3, d:10, e:11, f:12, g:7, h:8, i:9}, 
 
    result = []; 
 

 
function matched(i, j) { 
 
    return a[i]===o[j]; 
 
} 
 

 
for (var i=0; i<a.length; ++i) { 
 
    for (var j in o) { 
 
    if (matched(i, j)) 
 
     result.push(a[i]); // Store the found result. 
 
    } 
 
} 
 

 
var i = 0, 
 
    length = result.length; 
 

 
(function iterator() { 
 
    console.log(result[i]);   // Log the current result 
 

 
    if(++i < length) {    // If there are more entries in the array 
 
     setTimeout(iterator, 1000); // Log the next entry in 1 second. 
 
    } 
 
})();

+0

Cerbrus,謝謝,但那是我不滿意的解決方案:)。示例代碼中的數組非常簡單,但在真實應用程序中,匹配的集合可能變得非常大,所以如果存在不需要輸出到第三個數組的解決方案,我寧可不消耗更多的內存。 – 10basetom

+0

我們在說什麼_「非常大」_10basetom? – Cerbrus

+0

Cerbrus,在o中有數千個物品,在a中有數百個物品。可能有數百個匹配項,每個匹配項可能是一個大對象,所以我試圖儘可能地減少內存消耗。 – 10basetom

1

帶你的代碼,但改變輸出到push到另一個(全球)陣列。然後使用計時器逐個打印數組內容。

var a = [1, 2, 3, 4, 5, 6, 7, 8, 9], 
    o = {a:1, b:2, c:3, d:10, e:11, f:12, g:7, h:8, i:9}, 
    t = []; 

function matched(i, j) { 
    return a[i]===o[j]; 
} 

// function to output first element of array 
function output(){ 
    if(t.length > 0){ // only generate output if array t isn't empty 
    console.log(t.shift()); 
    setTimeout(output, 1000); // recall this function after 1s 
    } 
} 

for (var i=0; i<a.length; ++i) { 
    for (var j in o) { 
    if (matched(i, j)) t.push(a[i]); // store all found items inside the new array 
    } 
} 
output(); 
1

ES6解決方案,其中generator

function* getCommon(array, object) { 
 
    var set = new Set(array), k; 
 
    for (k in o) { 
 
     if (set.has(o[k])) { 
 
      yield o[k]; 
 
     } 
 
    }; 
 
} 
 

 
var a = [1, 2, 3, 4, 5, 6, 7, 8, 9], 
 
    o = { a: 1, b: 2, c: 3, d: 10, e: 11, f: 12, g: 7, h: 8, i: 9 }, 
 
    generator = getCommon(a, o), 
 
    interval = setInterval(function() { 
 
     var item = generator.next(); 
 
     if (item.done) { 
 
      clearInterval(interval); 
 
     } else { 
 
      console.log(item.value); 
 
     } 
 
    }, 1000);

+0

如果有10000個匹配項,則有10000個超時同時處於待處理狀態。這種方法可能是更大陣列的內存消耗。 – Cerbrus

+0

@Cerbrus,對。現在用setInterval。 –

+0

使用'Set'應該包含兼容性免責聲明。即使它只會被調用一次,你仍然在調用'setInterval' _。 – Cerbrus

-1

可以創建一個功能,其中包括電流forfor..in環形圖案,雖然僅迭代單a元件在for循環函數調用,同時保持現有for..in迴路;替代返回Promise遞歸調用函數,如果遞增變量小於a.length;否則用遞增變量遞歸地返回函數,或者如果變量是a.length,則在完成處理時返回值爲.then()

這種做法將使呼叫setTimeout()連續的,沒有產生在未來setTimeout一個等待受理,以函數下次調用僅僅是由當前setTimeout完成時,或歸還for循環的該迭代不匹配。

var a = [1, 2, 3, 4, 5, 6, 7, 8, 9] 
 
, o = {a: 1, b: 2, c: 3, d: 10, e: 11, f: 12, g: 7, h: 8, i:9}; 
 

 
function matched(i, j) { 
 
    return a[i] === o[j]; 
 
} 
 

 
function matcher(index = 0) { 
 
    var len = index < a.length; 
 
    for (var i = index; i < index + 1; ++i) { 
 
    for (var j in o) { 
 
     if (matched(i, j)) { 
 
     return new Promise(function(resolve) { 
 
      setTimeout(function() { 
 
      resolve(a[i]); 
 
      }, 1000) 
 
     }) 
 
     .then(function(result) { 
 
      console.log(`result:${result}`); 
 
      if (len) { 
 
      return matcher(++index) 
 
      } 
 
     }); 
 
     } 
 
    } 
 
    }; 
 
    return len ? matcher(++index) : "matcher complete"; 
 
} 
 

 
matcher().then(function(complete) { 
 
    console.log(complete); 
 
});

+0

您可能會同時產生數千個承諾,每個承諾都有一個待處理的超時。這將是一個記憶豬。 – Cerbrus