$ .When.apply ($, someArray) làm gì?


110

Tôi đang đọc về Trì hoãn và Lời hứa và tiếp tục xem qua $.when.apply($, someArray). Tôi hơi không rõ điều này chính xác làm gì, đang tìm lời giải thích rằng một dòng hoạt động chính xác (không phải toàn bộ đoạn mã). Đây là một số bối cảnh:

var data = [1,2,3,4]; // the ids coming back from serviceA
var processItemsDeferred = [];

for(var i = 0; i < data.length; i++){
  processItemsDeferred.push(processItem(data[i]));
}

$.when.apply($, processItemsDeferred).then(everythingDone); 

function processItem(data) {
  var dfd = $.Deferred();
  console.log('called processItem');

  //in the real world, this would probably make an AJAX call.
  setTimeout(function() { dfd.resolve() }, 2000);    

  return dfd.promise();
}

function everythingDone(){
  console.log('processed all items');
}

1
.done()có thể được sử dụng thay thế .thentrong trường hợp này, chỉ FYI
Kevin B

2
fwiw, có một cổng Deferred để gạch dưới cho phép chuyển một mảng duy nhất đến _.whenđể bạn không cần sử dụngapply
Eevee



1
Bài báo mà OP đề cập đến trong câu đầu tiên của anh ấy đã chuyển địa điểm - hiện tại là: flaviocopes.com/blog/deferreds-and-promises-in-javascript .
glaucon,

Câu trả lời:


161

.applyđược sử dụng để gọi một hàm với một mảng đối số. Nó nhận từng phần tử trong mảng và sử dụng từng phần tử làm tham số cho hàm. .applycũng có thể thay đổi ngữ cảnh ( this) bên trong một hàm.

Vì vậy, chúng ta hãy lấy $.when. Người ta thường nói "khi tất cả những lời hứa này được giải quyết ... hãy làm điều gì đó". Nó có một số lượng tham số (biến) vô hạn.

Trong trường hợp của bạn, bạn có một loạt các lời hứa; bạn không biết mình đang truyền bao nhiêu tham số $.when. Việc vượt qua chính mảng $.whensẽ không hoạt động, bởi vì nó mong đợi các tham số của nó là các hứa hẹn, không phải là một mảng.

Đó là nơi .applyxuất hiện. Nó lấy mảng và gọi $.whenmỗi phần tử làm tham số (và đảm bảo rằng giá trị thisđược đặt thành jQuery/ $), vì vậy tất cả đều hoạt động :-)


3
khi nhiều lời hứa được chuyển đến phương thức $ .when. Họ sẽ thực hiện theo thứ tự nào? cái này sau cái kia hay song song?
Darshan

21
@Darshan: Bạn không "chạy" những lời hứa. Bạn đợi chúng được giải quyết. Chúng được thực thi khi được tạo, $.whenchỉ cần đợi tất cả chúng hoàn thành trước khi tiếp tục.
Rocket Hazmat

1
những gì về sự khác biệt giữa $.when($, arrayOfPromises).done(...)$.when(null, arrayOfPromises).done(...) (mà tôi tìm thấy cả hai như là giải pháp được đề xuất trong các diễn đàn ...)
zeroquaranta

63

$ .when nhận bất kỳ số lượng tham số nào và giải quyết khi tất cả những tham số này đã được giải quyết.

anyFunction .apply (thisValue, arrayParameters) gọi hàm anyFunction cài đặt ngữ cảnh của nó (thisValue sẽ là cái này trong lệnh gọi hàm đó) và chuyển tất cả các đối tượng trong arrayParameters dưới dạng các tham số riêng lẻ.

Ví dụ:

$.when.apply($, [def1, def2])

Giống như:

$.when(def1, def2)

Nhưng cách gọi áp dụng cho phép bạn truyền một mảng các tham số không xác định. (Trong mã của bạn, bạn đang nói rằng dữ liệu của bạn đến từ một dịch vụ, thì đó là cách duy nhất để gọi $ .when )


15

Đây, mã được ghi đầy đủ.

// 1. Declare an array of 4 elements
var data = [1,2,3,4]; // the ids coming back from serviceA
// 2. Declare an array of Deferred objects
var processItemsDeferred = [];

// 3. For each element of data, create a Deferred push push it to the array
for(var i = 0; i < data.length; i++){
  processItemsDeferred.push(processItem(data[i]));
}

// 4. WHEN ALL Deferred objects in the array are resolved THEN call the function
//    Note : same as $.when(processItemsDeferred[0], processItemsDeferred[1], ...).then(everythingDone);
$.when.apply($, processItemsDeferred).then(everythingDone); 

// 3.1. Function called by the loop to create a Deferred object (data is numeric)
function processItem(data) {
  // 3.1.1. Create the Deferred object and output some debug
  var dfd = $.Deferred();
  console.log('called processItem');

  // 3.1.2. After some timeout, resolve the current Deferred
  //in the real world, this would probably make an AJAX call.
  setTimeout(function() { dfd.resolve() }, 2000);    

  // 3.1.3. Return that Deferred (to be inserted into the array)
  return dfd.promise();
}

// 4.1. Function called when all deferred are resolved
function everythingDone(){
  // 4.1.1. Do some debug trace
  console.log('processed all items');
}

7
$.when.apply($, array)không giống như $.when(array). Nó giống như:$.when(array[0], array[1], ...)
Rocket Hazmat

1
Đó là lý do chính tại sao được sử dụng với .apply , bạn không biết hoy nhiều phần tử processItemsDeferred có
Pablo

2

Thật không may, tôi không thể đồng ý với các bạn.

$.when.apply($, processItemsDeferred).always(everythingDone);

Sẽ gọi everythingDonengay khi một cuộc gọi hoãn bị từ chối , ngay cả khi có những cuộc gọi hoãn khác đang chờ xử lý .

Đây là tập lệnh đầy đủ (tôi khuyên bạn nên dùng http://jsfiddle.net/ ):

var data = [1,2,3,4]; // the ids coming back from serviceA
var processItemsDeferred = [];

for(var i = 0; i < data.length; i++){
  processItemsDeferred.push(processItem(data[i]));
}

processItemsDeferred.push($.Deferred().reject());
//processItemsDeferred.push($.Deferred().resolve());

$.when.apply($, processItemsDeferred).always(everythingDone); 

function processItem(data) {
  var dfd = $.Deferred();
  console.log('called processItem');

  //in the real world, this would probably make an AJAX call.
  setTimeout(function() { dfd.resolve(); }, 2000);    

  return dfd.promise();
}

function everythingDone(){
  alert('processed all items');
}

Nó là một lỗi? Tôi muốn sử dụng điều này giống như người đàn ông ở trên đã mô tả nó.


1
Từ chối đầu tiên sẽ kích hoạt luôn luôn, nhưng không kích hoạt .then. Xem jsfiddle.net/logankd/s5dacgb3 của tôi mà tôi đã tạo từ ví dụ của bạn. Tôi đang sử dụng JQuery 2.1.0 trong ví dụ này.
Căn chỉnh

1
Đây là như dự định. Có rất nhiều trường hợp mà bạn muốn biết ngay khi có điều gì đó không thành công, chứ không phải đợi mọi thứ hoàn thành và kiểm tra xem có lỗi nào không. Đặc biệt nếu quá trình xử lý không thể tiếp tục sau bất kỳ lỗi nào, tại sao phải đợi phần còn lại kết thúc / thất bại? Như nhận xét khác đã đề xuất, bạn có thể sử dụng cặp .then hoặc .fail & .done.
MPavlak

@GoneCoding Nó không hữu ích. OP đã hỏi ứng dụng apply () làm gì và bạn đã đề xuất một giải pháp thay thế khủng khiếp mà không bao giờ nên sử dụng :) đó là nút bỏ phiếu xuống dùng để làm gì. Tôi cũng không sử dụng nó cho đến khi bạn bị từ chối cung cấp TẠI SAO bạn đã làm nó rằng tại sao (hơn sở thích của bạn để mảng tránh đối với một số lý do)
MPavlak

@GoneCoding Cảm ơn bạn đã gỡ câu trả lời đó xuống
MPavlak

1
@GoneCoding lol, tôi đã đọc giải pháp của bạn và cung cấp phản hồi. bạn đã không cung cấp câu trả lời cho câu hỏi ban đầu. Bạn không thể giải thích tại sao nó lại như vậy. Chính những người như bạn cung cấp các giải pháp khủng khiếp cho những người đang học hỏi. Bạn rõ ràng có kỹ năng javascript hạn chế và đang coi tôi là n00b. Tôi đã chỉ ra lý do tại sao nó sai và bạn thậm chí không thể đọc mã và thay vào đó nói với tôi rằng tôi đã sai. bạn làm tốt lắm!
MPavlak

1

Có thể ai đó có thể thấy điều này hữu ích:

$.when.apply($, processItemsDeferred).then(everythingDone).fail(noGood);

everythingDone không được gọi trong trường hợp bị từ chối


0

$ .when một mình giúp cho việc gọi lại có thể được gọi khi mọi lời hứa được chuyển cho nó đều được giải quyết / từ chối. Thông thường, $ .w khi nhận một số lượng biến đối số, việc sử dụng .apply có thể truyền cho nó một mảng đối số, nó rất mạnh mẽ. Để biết thêm thông tin về .apply: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/apply


0

Cảm ơn vì giải pháp thanh lịch của bạn:

var promise;

for(var i = 0; i < data.length; i++){
  promise = $.when(promise, processItem(data[i]));
}

promise.then(everythingDone);

Chỉ một điểm: Khi sử dụng resolveWithđể lấy một số tham số, nó sẽ bị phá vỡ do lời hứa ban đầu được đặt thành không xác định. Những gì tôi đã làm để làm cho nó hoạt động:

// Start with an empty resolved promise - undefined does the same thing!
var promise;

for(var i = 0; i < data.length; i++){
  if(i==0) promise = processItem(data[i]);
  else promise = $.when(promise, processItem(data[i]));
}

promise.then(everythingDone);

2
Mặc dù điều đó có hiệu quả nhưng nó không thực sự thanh lịch. bạn đang tạo các lời hứa đại diện cho việc trì hoãn thứ i đang được thực hiện để lần lặp cuối cùng chứa "when (workToDo [0..i-1], workToDo [i])" hoặc rõ ràng hơn "khi tất cả công việc trước đó và điều này công việc đã xong ”. Điều này có nghĩa là bạn có i + 1 khi kết thúc lời hứa của mình. Ngoài ra, khi thực hiện loại nội dung này, chỉ cần mở vòng lặp đầu tiên. var Hứa hẹn = processItem (dữ liệu [0]); for (var i = 1; i <data.length; i ++) {promise = $ .when (promise, processItem (data [i])); }
MPavlak
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.