2011-11-26 76 views
6

我有一個異步函數,我想在觸發之前有一個5000ms的延遲。我正在嘗試使用setTimeout()來實現這一點。這個異步函數發生在多次運行的循環中,異步函數每次傳遞不同的數據,因此setInterval()不能在這裏使用。setTimeout on異步函數

問題:異步函數被瞬間觸發無延遲(控制檯打印5個Done消息instantly`,沒有任何延遲循環發生了什麼,我怎麼能解決這個問題

JavaScript代碼

someFunction(listings, function() { 
    for (var i in listings) { 
     var listing = listings[i]; 
     setTimeout(asyncFunction(listing, function(data) { 
      console.log('Done'); 
     }), 5000); 
    } 
}); 
+0

如果「列表」實際上是一個數組,則不應該用「for ... in」進行迭代。改用數字索引。 – Pointy

+0

@Pointy是的,它只是一個數組,哎呀:) – Nyxynyx

回答

9

你必須將函數包裝到另一個函數中,目前你正在調用該函數,並將返回值作爲參數傳遞給setTimeout下面的代碼將(correc tly)將功能傳遞給setTimeout。 5秒後,該功能執行。

我不得不添加兩個功能,以實現所需的行爲,因爲範圍問題。 5秒後,循環已經完成,並且listing變量將等於listings中的最後一個元素。

someFunction(listings, function() { 
    var counter = 0; // Define counter for 5 second-delays between each call 
    for (var i in listings) { 
     var listing = listings[i]; 
     (function(listing){ //Closure function 
      setTimeout(function(){ //setTimeout function 
       // Because of the closure, `listing` is unique 
       asyncFunction(listing, function(a, b) { 
        console.log('Done'); 
       }); 
      }, 5000 * ++counter); //Increase counter for each loop 
     })(listing); 
    } 
}); 
+0

這仍然會觸發n個超時幾乎平行。 – jAndy

+0

感謝您的回覆!初始5000ms延遲,但隨後對asyncFunction的調用立即發生!隨後對這些功能的呼叫是否會延遲,使每個呼叫間隔5000毫秒? – Nyxynyx

+0

它也有經典的循環/範圍問題(「列表」的變量) – Pointy

-1

這是差別。關鍵是什麼是asyncFunction做什麼?你可以把它粘貼出來嗎?

var foo=function(){ 
    alert("BAR"); 
    return function(){ 
     alert("I AM!"); 
    }; 
} 
setTimeout(foo(),4000); 
setTimeout(foo,5000); 
+4

第一個「setTimeout()」不正確,因爲它會立即調用「foo()」並將其*結果*傳遞給「setTimeout()」。第二,傳遞一個字符串,通常被認爲是棄用的。 – Pointy

+0

是的,傳遞字符串會''評估它,然後在全局範圍上運行它。非常不好的主意 – Rifat

+0

非常感謝你! – island205

1

不知道你asyncFunction做什麼,它似乎是它可以簡單的返回你通過它的功能。

someFunction(listings, function() { 
    for (var i = 0; i < listings.length; ++i) { 
     setTimeout(asyncFunction(listings[i], function(data) { 
      console.log('Done'); 
     }), 5000 * i); 
    } 
}); 

function asyncFunction(lstng, func) { 
    return func; 
} 

雖然我希望你需要包裝一些額外的邏輯。無論是需要在返回到setTimeout新功能

function asyncFunction(lstng, func) { 
    return function() { 
     // do some stuff with the listing 

     // then invoke the func 
     func(); 
    } 
} 

現在你asyncFunction套。新函數還會調用您傳遞的回調。


JSFIDDLE DEMO

+0

謝謝,通過使用'i'而不是'counter'來減少一行代碼。 asyncFunction在'data'中使用2個回調值並將它們發送到另一個服務器。 – Nyxynyx

+0

@Nyxynyx:不完全確定你的意思,但你可以將你需要的任何東西傳遞給回調函數。我的主要觀點是你已經有了一個命名函數,所以不需要添加兩層內聯範圍。你只需要修改'asyncFunction',以便它*返回一個函數,由'setTimeout'調用。這是一個更清潔的方法。 – RightSaidFred

4

如果您正在使用ECMAScript6可以使用Promise

所以創建包裹調用setTimeout的成無極延遲功能:

function delay(ms) { 
    return new Promise(function (resolve) { return setTimeout(resolve, ms); }); 
}; 

而且你可以用它這樣的:

someFunction(listings, function() { 
    for (var i in listings) { 
     var listing = listings[i]; 
     delay(5000).then(() => { 
      return asyncFunction(listing); 
     }).then(() => { 
      console.log('Done'); 
     }); 
    } 
}); 

如果您正在使用ECMAScript 2017可以使用aync/await

異步函數返回承諾,因此您不必更改延遲函數的代碼。

async someFunction(listings, function() { 
    for (var i in listings) { 
     var listing = listings[i]; 
     await delay(5000); 
     await asyncFunction(listing); 
     console.log('Done'); 
    } 
});