$ .when.apply ($, someArray) ทำอะไร?


110

ฉันอ่านเกี่ยวกับ Deferreds และสัญญา$.when.apply($, someArray)และให้มาข้าม ฉันไม่ชัดเจนเล็กน้อยว่าสิ่งนี้ทำอะไรได้บ้างโดยกำลังมองหาคำอธิบายว่าหนึ่งบรรทัดทำงานได้ตรงกันทั้งหมด (ไม่ใช่ข้อมูลโค้ดทั้งหมด) นี่คือบริบทบางส่วน:

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()สามารถใช้แทน.thenในกรณีนี้ได้เฉพาะ FYI
Kevin B

2
fwiw มีพอร์ตรอการตัดทอนเพื่อขีดเส้นใต้ที่อนุญาตให้ส่งอาร์เรย์เดียวไปได้โดยที่_.whenคุณไม่จำเป็นต้องใช้apply
Eevee

เรียนรู้เพิ่มเติมเกี่ยวกับ.apply: developer.mozilla.org/en-US/docs/JavaScript/Reference/...
Felix Kling


1
บทความ OP หมายถึงในประโยคแรกของเขามีสถานที่ตั้งย้าย - ก็ตอนนี้ได้ที่: flaviocopes.com/blog/deferreds-and-promises-in-javascript
glaucon

คำตอบ:


161

.applyใช้เพื่อเรียกใช้ฟังก์ชันด้วยอาร์เรย์ของอาร์กิวเมนต์ ใช้แต่ละองค์ประกอบในอาร์เรย์และใช้แต่ละองค์ประกอบเป็นพารามิเตอร์ของฟังก์ชัน .applyยังสามารถเปลี่ยนบริบท ( this) ภายในฟังก์ชัน

$.whenดังนั้นลองมา ใช้เพื่อพูดว่า "เมื่อคำสัญญาเหล่านี้ได้รับการแก้ไข ... ทำบางสิ่ง" ต้องใช้จำนวนพารามิเตอร์ไม่ จำกัด (ตัวแปร)

ในกรณีของคุณคุณมีสัญญามากมาย คุณไม่รู้ว่าคุณส่งผ่านพารามิเตอร์ไป$.whenกี่ตัว การส่งอาร์เรย์ไปเอง$.whenจะไม่ได้ผลเพราะคาดว่าพารามิเตอร์จะเป็นสัญญาไม่ใช่อาร์เรย์

นั่นคือที่.applyมามันใช้อาร์เรย์และเรียก$.whenใช้แต่ละองค์ประกอบเป็นพารามิเตอร์ (และตรวจสอบให้แน่ใจว่าthisตั้งค่าเป็นjQuery/ $) ดังนั้นทุกอย่างก็ใช้ได้ :-)


3
เมื่อมีการส่งต่อสัญญาหลายฉบับไปยัง $. เมื่อวิธีการ พวกเขาจะดำเนินการตามลำดับใด หนึ่งต่อจากนั้นหรือขนานกัน?
Darshan

21
@ ดาร์ชัน: คุณไม่ได้ "วิ่ง" สัญญา คุณรอให้พวกเขาได้รับการแก้ไข พวกเขาจะถูกดำเนินการเมื่อสร้างขึ้น$.whenเพียงรอให้ทั้งหมดเสร็จสิ้นก่อนดำเนินการต่อ
Rocket Hazmat

1
สิ่งที่เกี่ยวกับความแตกต่างระหว่าง $.when($, arrayOfPromises).done(...) และ $.when(null, arrayOfPromises).done(...) (ซึ่งฉันพบทั้งสองอย่างตามวิธีแก้ปัญหาที่เสนอในฟอรัม ... )
zeroquaranta

63

$. เมื่อรับพารามิเตอร์จำนวนเท่าใดก็ได้และแก้ไขเมื่อสิ่งเหล่านี้ได้รับการแก้ไขแล้ว

anyFunction .apply (thisValue, arrayParameters) เรียกใช้ฟังก์ชันanyFunctionการตั้งค่าบริบท (thisValue จะเป็นค่านี้ภายในการเรียกฟังก์ชันนั้น) และส่งผ่านวัตถุทั้งหมดใน arrayParameters เป็นพารามิเตอร์แต่ละตัว

ตัวอย่างเช่น:

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

เหมือนกับ:

$.when(def1, def2)

แต่วิธีใช้การโทรช่วยให้คุณสามารถส่งผ่านอาร์เรย์ของจำนวนพารามิเตอร์ที่ไม่รู้จักได้ (ในรหัสของคุณคุณกำลังบอกว่าข้อมูลของคุณมาจากบริการนั่นเป็นวิธีเดียวที่จะโทรไปที่$ .when )


15

ที่นี่รหัสมีเอกสารครบถ้วน

// 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)คือไม่ได้$.when(array)เช่นเดียวกับ มันเหมือนกับ:$.when(array[0], array[1], ...)
Rocket Hazmat

1
นั่นเป็นเหตุผลหลักว่าทำไมจะใช้กับ.applyคุณไม่ทราบว่าเฮ้ยหลายองค์ประกอบ processItemsDeferred มี
ปาโบล

2

น่าเสียดายที่ฉันไม่สามารถตกลงกับพวกคุณได้

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

จะโทรeverythingDoneทันทีที่ถูกปฏิเสธการรอการตัดบัญชีแม้ว่าจะมีรายการรอการตัดบัญชีอื่น ๆ ที่รอดำเนินการอยู่ก็ตาม

นี่คือสคริปต์ฉบับเต็ม (ฉันแนะนำ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');
}

มันเป็นข้อผิดพลาด? ฉันต้องการใช้สิ่งนี้เหมือนสุภาพบุรุษที่อธิบายไว้ข้างต้น


1
การปฏิเสธครั้งแรกจะเริ่มต้นเสมอ แต่ไม่ใช่จากนั้น ดูjsfiddle.net/logankd/s5dacgb3ของฉันที่ฉันสร้างจากตัวอย่างของคุณ ฉันใช้ JQuery 2.1.0 ในตัวอย่างนี้
จัด

1
นี่เป็นไปตามที่ตั้งใจไว้ มีหลายกรณีที่คุณต้องการทราบทันทีที่มีบางอย่างล้มเหลวอย่ารอให้ทุกอย่างเสร็จสมบูรณ์และตรวจสอบว่ามีข้อผิดพลาดหรือไม่ โดยเฉพาะอย่างยิ่งหากการประมวลผลไม่สามารถดำเนินต่อไปได้หลังจากเกิดความล้มเหลวทำไมต้องรอให้ส่วนที่เหลือเสร็จสิ้น / ล้มเหลว ตามที่ความคิดเห็นอื่น ๆ แนะนำคุณสามารถใช้คู่. then หรือ. fail & .done
MPavlak

@GoneCoding มันไม่มีประโยชน์ OP ถามว่าใช้ () ทำอะไรและคุณแนะนำทางเลือกที่แย่มากที่ไม่ควรใช้ :) นั่นคือปุ่มโหวตลงสำหรับ ฉันไม่ได้ใช้มันจนกว่าคุณจะปฏิเสธที่จะให้เหตุผลว่าทำไมคุณถึงทำเช่นนั้นทำไม (มากกว่าที่คุณต้องการที่จะหลีกเลี่ยงอาร์เรย์ด้วยเหตุผลบางประการ)
MPavlak

@GoneCoding ขอบคุณที่ตอบคำตอบนั้น
MPavlak

1
@GoneCoding ฮ่า ๆ ฉันอ่านวิธีแก้ปัญหาของคุณและให้ข้อเสนอแนะ คุณไม่ได้ให้คำตอบสำหรับคำถามเดิม คุณไม่สามารถอธิบายได้อย่างละเอียดว่าเหตุใดจึงเป็นเช่นนั้น คนอย่างคุณนี่แหละที่ให้วิธีแก้ปัญหาที่แย่มากสำหรับคนที่กำลังเรียนรู้ คุณมีทักษะจาวาสคริปต์ที่ จำกัด อย่างชัดเจนและกำลังพาฉันไปเป็น n00b ฉันระบุว่าทำไมจึงผิดและคุณอ่านรหัสไม่ได้และบอกฉันว่าฉันผิดแทน เพื่อนเก่ง!
MPavlak

1

อาจมีคนพบว่าสิ่งนี้มีประโยชน์:

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

ทุกอย่างไม่ถูกเรียกในกรณีที่ถูกปฏิเสธ


0

$. เมื่ออยู่คนเดียวทำให้สามารถโทรกลับได้เมื่อทุกคำสัญญาที่ส่งผ่านไปได้รับการแก้ไข / ปฏิเสธ โดยปกติ $. เมื่อรับอาร์กิวเมนต์เป็นจำนวนตัวแปรโดยใช้ .apply ทำให้สามารถส่งผ่านอาร์เรย์ของอาร์กิวเมนต์ได้จะมีประสิทธิภาพมาก สำหรับข้อมูลเพิ่มเติมเกี่ยวกับ. application: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/apply


0

ขอบคุณสำหรับโซลูชันที่สวยงามของคุณ:

var promise;

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

promise.then(everythingDone);

เพียงจุดเดียว: เมื่อใช้resolveWithเพื่อรับพารามิเตอร์บางอย่างมันจะแตกเนื่องจากสัญญาเริ่มต้นที่ตั้งค่าเป็นไม่ได้กำหนด สิ่งที่ฉันทำเพื่อให้ได้ผล:

// 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
แม้ว่าจะได้ผล แต่ก็ไม่สง่างามจริงๆ คุณกำลังสร้างสัญญาที่แสดงถึงการดำเนินการที่รอการตัดบัญชีเพื่อให้การทำซ้ำครั้งสุดท้ายประกอบด้วย "เมื่อ (workToDo [0..i-1], workToDo [i])" หรือชัดเจนกว่า "เมื่องานก่อนหน้านี้ทั้งหมดและสิ่งนี้ งานเสร็จแล้ว ". ซึ่งหมายความว่าคุณมี i + 1 เมื่อห่อหุ้มคำสัญญาของคุณ นอกจากนี้เมื่อทำสิ่งนี้เพียงแค่แกะการทำซ้ำครั้งแรก var สัญญา = processItem (ข้อมูล [0]); สำหรับ (var i = 1; i <data.length; i ++) {สัญญา = $. เมื่อ (สัญญา, processItem (ข้อมูล [i])); }
MPavlak
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.