2013-03-19 114 views
20

我想創建我認爲被稱爲「瀑布」的東西。我想順序處理一組異步函數(jQuery promise)。jQuery的異步循環延期(承諾)

下面是一個人爲的例子:

function doTask(taskNum){ 
    var dfd = $.Deferred(), 
     time = Math.floor(Math.random()*3000); 

    setTimeout(function(){ 
     console.log(taskNum); 
     dfd.resolve(); 
    },time) 

    return dfd.promise(); 
} 

var tasks = [1,2,3]; 

for (var i = 0; i < tasks.length; i++){ 
    doTask(tasks[i]); 
} 

console.log("all done"); 

我想它在完成它們被執行的順序(存在於陣列中)的任務。因此,在這個例子中,我希望它執行任務1並等待它解決,然後執行任務2等待它解決,執行任務3等並記錄「全部完成」。

也許這真的很明顯,但我一直試圖弄清楚這一點下午。

回答

19

我會嘗試使用$().queue而不是$.Deferred這裏。將函數添加到隊列中,只有在準備好時才調用下一個函數。

function doTask(taskNum, next){ 
    var time = Math.floor(Math.random()*3000); 

    setTimeout(function(){ 
     console.log(taskNum); 
     next(); 
    },time) 
} 

function createTask(taskNum){ 
    return function(next){ 
     doTask(taskNum, next); 
    } 
} 

var tasks = [1,2,3]; 

for (var i = 0; i < tasks.length; i++){ 
    $(document).queue('tasks', createTask(tasks[i])); 
} 

$(document).queue('tasks', function(){ 
    console.log("all done"); 
}); 

$(document).dequeue('tasks'); 
+1

這是一種!爲我解決了一個非常棘手的問題。非常感謝你。 – 2015-11-04 10:52:48

0

確實有趣的挑戰。我想到的是一個遞歸函數,它接受一個列表和一個可選的開始索引。

Here is a link to the jsFiddle我已經測試了幾個不同的列表長度和間隔。

我假設你有一個返回promise(不是數字列表)的函數列表。如果你有一個數字的列表,你會改變這部分

$.when(tasks[index]()).then(function(){ 
    deferredSequentialDo(tasks, index + 1); 
}); 

這個

/* Proxy is a method that accepts the value from the list 
    and returns a function that utilizes said value 
    and returns a promise */ 
var deferredFunction = myFunctionProxy(tasks[index]); 

$.when(tasks[index]()).then(function(){ 
    deferredSequentialDo(tasks, index + 1); 
}); 

我不知道有多大的功能列表可以,但要知道,瀏覽器會保持第一個延遲的順序接入呼叫的資源,直到它們全部完成。

+0

* deferredSync *聽起來有點矛盾, – Bergi 2013-03-19 17:27:31

+0

確實如此,但是我能看到這樣的使用,如果你有一對夫婦的Ajax調用要同步執行(這是我有實際上碰到了我的經驗) – awbergs 2013-03-19 17:28:28

+0

Ajax *是*不同步。你什麼意思? – Bergi 2013-03-19 17:30:34

9

對於一個瀑布,你需要一個異步循環:

(function step(i, callback) { 
    if (i < tasks.length) 
     doTask(tasks[i]).then(function(res) { 
      // since sequential, you'd usually use "res" here somehow 
      step(i+1, callback); 
     }); 
    else 
     callback(); 
})(0, function(){ 
    console.log("all done"); 
}); 
4

看一看在$.whenthen方法運行deferreds。

瀑布用於管道返回值從一個延期到下一個串聯。它看起來像like this

function doTask (taskNum) { 
    var dfd = $.Deferred(), 
     time = Math.floor(Math.random() * 3000); 

    console.log("running task " + taskNum); 

    setTimeout(function(){ 
     console.log(taskNum + " completed"); 
     dfd.resolve(taskNum + 1); 
    }, time) 

    return dfd.promise(); 
} 

var tasks = [1, 2, 3]; 

tasks 
    .slice(1) 
    .reduce(function(chain) { return chain.then(doTask); }, doTask(tasks[0])) 
    .then(function() { console.log("all done"); }); 

注意傳遞給resolve的參數。這會傳遞給鏈中的下一個函數。如果你只是想串聯運行它們沒有參數的管道,你可以說出來和減少通話更改爲.reduce(function(chain, taskNum) { return chain.then(doTask.bind(null, taskNum)); }, doTask(tasks[0]));

而在平行它看起來like this

var tasks = [1,2,3].map(function(task) { return doTask(task); }); 

$.when.apply(null, tasks).then(function() { 
    console.log(arguments); // Will equal the values passed to resolve, in order of execution. 
}); 
+0

謝謝gumballhead,這對我來說是個詭計。 – CrystalVisions 2015-04-14 05:54:00

+0

這個答案實際上並不使用任務中的值。任務可以是[1,5,14],並仍然產生相同的輸出 – thedarklord47 2015-12-03 21:35:14

5

您可以創建一個已解決的$。遞延,只是加入連鎖與每次迭代:

var dfd = $.Deferred().resolve(); 
tasks.forEach(function(task){ 
    dfd = dfd.then(function(){ 
     return doTask(task); 
    }); 
}); 

一步以下步驟正在發生的事情:

//begin the chain by resolving a new $.Deferred 
var dfd = $.Deferred().resolve(); 

// use a forEach to create a closure freezing task 
tasks.forEach(function(task){ 

    // add to the $.Deferred chain with $.then() and re-assign 
    dfd = dfd.then(function(){ 

     // perform async operation and return its promise 
     return doTask(task); 
    }); 

}); 

就個人而言,我覺得比遞歸這種清潔,不是$()更熟悉的隊列。 (對於$().jquery API來說,由於它是爲動畫設計的,因此它可能會讓你感到困惑,也可能你在代碼的其他地方使用了$ .Deferred's)。它還具有在異步操作中通過resolve()將瀑布結果向下傳遞的功能,並允許附加$ .done屬性。

這是一個jsFiddle

+0

真的很喜歡這個,謝謝! – VeldMuijz 2017-03-30 09:07:16