2013-03-19 38 views
20

Tôi đang cố gắng tạo ra những gì tôi nghĩ được gọi là "Thác". Tôi muốn tuần tự xử lý một mảng các hàm async (lời hứa jQuery).Vòng lặp không đồng bộ của jQuery hoãn (hứa hẹn)

Dưới đây là một ví dụ contrived:

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"); 

Tôi muốn nó để hoàn thành nhiệm vụ theo thứ tự chúng được thực hiện (hiện diện trong mảng). Vì vậy, trong ví dụ này tôi muốn nó làm nhiệm vụ 1 và chờ cho nó giải quyết sau đó làm nhiệm vụ 2 chờ cho nó để giải quyết, làm nhiệm vụ 3 vv và đăng nhập "tất cả được thực hiện".

Có lẽ điều này thực sự rõ ràng nhưng tôi đã cố gắng tìm ra điều này trong tất cả các buổi chiều.

Trả lời

19

Tôi muốn thử sử dụng $().queue thay vì $.Deferred tại đây. Thêm các hàm vào hàng đợi và chỉ gọi hàm tiếp theo khi sẵn sàng.

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

đây là một trong một loại! giải quyết một vấn đề rất khó đối với tôi. Cảm ơn nhiều. –

0

Thử thách thú vị thực sự. Những gì tôi đã đưa ra là một hàm đệ quy chấp nhận một danh sách và một chỉ mục bắt đầu tùy chọn.

Here is a link to the jsFiddle mà tôi đã thử nghiệm với một vài độ dài và khoảng thời gian danh sách khác nhau.

Tôi giả sử bạn có danh sách các hàm trả về lời hứa (không phải danh sách số). Nếu bạn có một danh sách các số bạn sẽ thay đổi phần này

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

này

/* 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); 
}); 

Tôi không chắc chắn lớn như thế nào danh sách các chức năng có thể nhưng chỉ cần lưu ý rằng trình duyệt sẽ giữ các tài nguyên từ cuộc gọi deferredSequentialDo đầu tiên cho đến khi chúng được hoàn thành.

+0

* deferredSync * Nghe có vẻ như một nghịch lý – Bergi

+0

Nó, tuy nhiên tôi có thể thấy một sử dụng cho điều này nếu bạn có một vài ajax gọi mà bạn muốn thực hiện đồng bộ (cái gì tôi có trên thực tế, trải nghiệm của tôi) – awbergs

+0

Ajax * là * không đồng bộ. Ý anh là gì? – Bergi

9

Đối với một thác nước, bạn cần một vòng lặp async:

(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

Có một cái nhìn tại $.whenthen phương pháp để chạy deferreds.

Thác nước được sử dụng để trả lại giá trị đường ống từ một giá trị trả về cho đến chuỗi tiếp theo. Nó sẽ trông giống như một cái gì đó 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"); }); 

Lưu ý đối số được chuyển đến resolve. Điều đó được chuyển đến hàm tiếp theo trong chuỗi. Nếu bạn chỉ muốn chạy chúng trong loạt bài mà không cần đường ống trong lập luận, bạn có thể đi mà ra và thay đổi làm giảm cuộc gọi đến .reduce(function(chain, taskNum) { return chain.then(doTask.bind(null, taskNum)); }, doTask(tasks[0]));

Và song song nó sẽ trông 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

Cảm ơn bạn gumballhead, điều này đã làm các trick cho tôi. – CrystalVisions

+0

câu trả lời này không thực sự sử dụng các giá trị trong nhiệm vụ. nhiệm vụ có thể là [1,5,14] và vẫn sản xuất cùng một đầu ra – thedarklord47

5

Bạn có thể tạo $ đã giải quyết.Hoãn và chỉ cần thêm vào chuỗi với mỗi lần lặp:

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

Từng bước sau đây đang xảy ra:

//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); 
    }); 

}); 

Cá nhân, tôi tìm thấy điều này sạch hơn đệ quy và nhiều hơn nữa quen thuộc hơn $() hàng đợi. (jQuery API cho $(). Hàng đợi là khó hiểu vì nó được thiết kế cho hình ảnh động, nó cũng có khả năng bạn đang sử dụng $ .Deferred ở những nơi khác trong mã của bạn). Nó cũng có lợi ích của việc chuyển giao các kết quả tiêu chuẩn xuống thác thông qua giải quyết() trong hoạt động không đồng bộ và cho phép đính kèm thuộc tính $ .done.

Dưới đây là trong một jsFiddle

+0

Thực sự thích cái này, cảm ơn! – VeldMuijz

Các vấn đề liên quan