เรียกใช้ฟังก์ชันหลังจากฟังก์ชันก่อนหน้านี้เสร็จสมบูรณ์


179

ฉันมีรหัส JavaScript ต่อไปนี้:

$('a.button').click(function(){
    if (condition == 'true'){
        function1(someVariable);
        function2(someOtherVariable);
    }
    else {
        doThis(someVariable);
    }
});

ฉันจะมั่นใจได้อย่างไรว่าfunction2ถูกเรียกหลังจากfunction1เสร็จสิ้นแล้ว?


24
กำลังfunction1ทำการดำเนินการ async
LiraNuna

8
ใช่ถ้า function1 มีภาพเคลื่อนไหว function2 จะถูกดำเนินการในขณะที่ภาพเคลื่อนไหวของ function1 ยังคงเกิดขึ้น คุณจะทำให้ function2 รอจนกระทั่งทุกอย่างที่เริ่มต้นใน function1 เสร็จสมบูรณ์อย่างไร
trusktr

ใครสามารถอธิบายให้ฉันฟังได้ว่าทำไมต้องทำอะไรเพื่อให้แน่ใจว่าไม่เรียกใช้ function2 จนกว่า function1 จะเสร็จสมบูรณ์ ไม่มีการดำเนินการแบบอะซิงโครนัสเกิดขึ้นที่นี่ดังนั้น function2 จะไม่ทำงานจนกว่า function1 จะเสร็จสมบูรณ์ต่อไปเนื่องจากการดำเนินการนี้แบบซิงโครนัส
แอรอน

คำตอบ:


206

ระบุการโทรกลับแบบไม่ระบุชื่อและให้ function1 ยอมรับ:

$('a.button').click(function(){
    if (condition == 'true'){
        function1(someVariable, function() {
          function2(someOtherVariable);
        });
    }
    else {
        doThis(someVariable);
    }
});


function function1(param, callback) {
  ...do stuff
  callback();
} 

11
+1 แต่ถ้าเขาใช้ 1.5 เขาก็สามารถใช้รูปแบบ Deferreds ใหม่ได้
philwinkle

ขอบคุณสำหรับการตอบกลับอย่างรวดเร็ว ใช่ฟังก์ชั่น 1 กำลังทำงานง่าย ๆ แบบอะซิงโครนัสดังนั้นฉันเชื่อว่าคุณถูกต้องในการที่ฉันจะต้องโทรกลับ - หรือดูที่ธุรกิจรอการตัดบัญชีที่กล่าวถึง ขอบคุณอีกครั้งนะ ฉันจะนอนหลับและจัดการเรื่องนี้อีกครั้งในตอนเช้า
Rrryyyaaannn

13
คำตอบนี้ไม่ใช่ทางออก นี่คือข้อพิสูจน์ว่าใช้งานไม่ได้: jsfiddle.net/trusktr/M2h6e
trusktr

12
@MikeRobinson นี่คือปัญหา: เกิดอะไรขึ้นถ้า...do stuffมีสิ่งที่ไม่ตรงกัน? Function2 จะยังคงทำงานไม่ได้ตามที่คาดไว้ ตัวอย่างในความคิดเห็นของฉันด้านบนแสดงให้เห็นว่าเนื่องจากfoo()ฟังก์ชั่นมีรหัสแบบอะซิงโครนัสอยู่ในนั้นbar()อย่าเปิดใช้งานเนื้อหาหลังจากfoo()มีการดำเนินการของเนื้อหาแม้ว่าจะใช้$.when().then()เพื่อเรียกพวกเขาไปอีกแบบ คำตอบนี้ใช้ได้เฉพาะเมื่อ (และเฉพาะในกรณี) ทุกสิ่งที่...do stuffอยู่ในรหัสของคุณซิงโครนัสอย่างเคร่งครัด
trusktr

1
@trusktr ในกรณีที่คุณต้องการเพื่อให้ผ่านการเรียกกลับครั้งแรกที่ลงไปnระดับของการโทรไม่ตรงกัน, th แล้วไฟcallBack()หลังจากทั้งหมดnตรงกันสายจะทำ เนื่องจาก OP ไม่ได้กล่าวถึงสิ่งที่เขาทำในฟังก์ชั่นมันจึงสันนิษฐานว่ามันเป็นการเรียกแบบอะซิงโครนัสระดับหนึ่ง
GoodSp33d

96

หากคุณใช้ jQuery 1.5 คุณสามารถใช้รูปแบบ Deferreds ใหม่:

$('a.button').click(function(){
    if(condition == 'true'){
        $.when(function1()).then(function2());
    }
    else {
        doThis(someVariable);
    }
});

แก้ไข: ลิงค์บล็อกที่อัปเดต:

Rebecca Murphy มีบทความที่ยอดเยี่ยมมากมายที่นี่: http://rmurphey.com/blog/2010/12/25/deferreds-coming-to-jquery/


2
แต่นั่นเป็นตัวอย่างการทำงานหรือไม่ สิ่งที่ถูกเลื่อนออกไป? คุณกำลังเรียกใช้งาน function1 และ function2 ทันที (ฉันไม่ใช่ผู้ให้ความเห็นดั้งเดิม)
user113716

10
นั่นไม่ทำงานเลยสำหรับฉัน function1 และ function2 ทั้งคู่ดำเนินการในเวลาเดียวกัน (ตามที่สังเกตได้ด้วยสายตามนุษย์) นี่คือตัวอย่างเล็ก ๆ : jsfiddle.net/trusktr/M2h6e
trusktr

1
การเขียนบทความของ Rebecca Murphy นั้นก่อนที่ Defferds จะได้รับการแนะนำให้รู้จักกับ jQuery และใช้ตัวอย่างตามวิธีที่นักเขียนคิดว่าจะนำไปใช้ กรุณาเยี่ยมชมเว็บไซต์ของ jQuery เพื่อดูวิธีการใช้คุณสมบัตินี้: api.jquery.com/jQuery.Deferred
Spencer Williams

1
แม้จะใช้ jQuery 1.5 หรืออะไรที่ทันสมัยคุณไม่ต้องการ$.when(function1).then(function2);(ไม่มีวงเล็บที่เรียกฟังก์ชัน) ใช่ไหม
Teepeemm

1
อ่านความคิดเห็นเหล่านี้ไม่กี่ปีที่ผ่านมา function1ควรคืนสัญญา ในกรณีนั้นจะต้องเป็นฟังก์ชันที่ดำเนินการจริงไม่ใช่การอ้างอิง เมื่อresolveถูกเรียกตามสัญญานั้นfunction2จะถูกดำเนินการ สิ่งนี้สามารถอธิบายได้ง่ายโดยสมมติว่าfunction1มีการโทร ajax การdoneโทรกลับจะแก้ไขสัญญา ฉันหวังว่าชัดเจน
philwinkle

46

ลองสิ่งนี้:

function method1(){
   // some code

}

function method2(){
   // some code
}

$.ajax({
   url:method1(),
   success:function(){
   method2();
}
})

37

คำตอบนี้ใช้promisesคุณสมบัติ JavaScript ของECMAScript 6มาตรฐาน หากแพลตฟอร์มเป้าหมายของคุณไม่สนับสนุนpromises, Polyfill กับPromiseJs

สัญญาเป็นวิธีใหม่ (และดีกว่ามาก) ในการจัดการการดำเนินงานแบบอะซิงโครนัสใน JavaScript:

$('a.button').click(function(){
    if (condition == 'true'){
        function1(someVariable).then(function() {
            //this function is executed after function1
            function2(someOtherVariable);
        });
    }
    else {
        doThis(someVariable);
    }
});


function function1(param, callback) {
    return new Promise(function (fulfill, reject){
        //do stuff
        fulfill(result); //if the action succeeded
        reject(error); //if the action did not succeed
    });
} 

นี่อาจดูเหมือนเป็นค่าใช้จ่ายที่สำคัญสำหรับตัวอย่างง่ายๆนี้ แต่สำหรับรหัสที่ซับซ้อนกว่านั้นดีกว่าการใช้การเรียกกลับ คุณสามารถเชื่อมโยงการโทรแบบอะซิงโครนัสหลายสายได้อย่างง่ายดายโดยใช้thenคำสั่งหลายรายการ:

function1(someVariable).then(function() {
    function2(someOtherVariable);
}).then(function() {
    function3();
});

นอกจากนี้คุณยังสามารถตัด jQuery deferrds ได้อย่างง่ายดาย (ซึ่งถูกส่งคืนจากการ$.ajaxโทร):

Promise.resolve($.ajax(...params...)).then(function(result) {
    //whatever you want to do after the request
});

ตามที่ @charlietfl ตั้งข้อสังเกตjqXHRวัตถุที่ส่งกลับโดย$.ajax()ใช้Promiseอินเทอร์เฟซ ดังนั้นจึงไม่จำเป็นต้องห่อไว้ใน a Promiseสามารถใช้โดยตรง:

$.ajax(...params...).then(function(result) {
    //whatever you want to do after the request
});

Promise.resolveห่อ$.ajaxเป็นรูปแบบการป้องกันตั้งแต่$.ajaxผลตอบแทน thennable สัญญา
charlietfl

@ charlietfl แต่มันไม่ใช่ของจริงPromiseมันแค่ใช้อินเทอร์เฟซ ฉันไม่แน่ใจว่ามันจะทำงานอย่างไรเมื่อส่งผ่านของจริงPromiseไปยังthenวิธีการของมันหรือสิ่งที่Promise.allจะทำถ้า a jqXHRและ a Promiseจะถูกส่งไปยังมัน เพื่อที่ฉันจะต้องทำการทดสอบเพิ่มเติม แต่ขอบคุณสำหรับคำใบ้ฉันจะเพิ่มไปยังคำตอบ!
Domysee

ที่จริงแล้วใน jQuery เวอร์ชัน 3+ นั้นเป็นสัญญาที่สอดคล้องกับ A + โดยไม่คำนึงถึง$.ajax.thenไม่ใหม่
charlietfl

21

หรือคุณสามารถทริกเกอร์เหตุการณ์ที่กำหนดเองเมื่อหนึ่งฟังก์ชั่นเสร็จสมบูรณ์จากนั้นผูกกับเอกสาร:

function a() {
    // first function code here
    $(document).trigger('function_a_complete');
}

function b() {
    // second function code here
}

$(document).bind('function_a_complete', b);

การใช้วิธีนี้ฟังก์ชัน 'b' สามารถเรียกใช้งานฟังก์ชันหลัง 'a' เท่านั้นเนื่องจากทริกเกอร์จะมีอยู่เมื่อฟังก์ชัน a ดำเนินการเสร็จสิ้น


"ตั้งแต่ jQuery 1.7 เมธอด. on () เป็นวิธีที่ต้องการสำหรับการแนบตัวจัดการเหตุการณ์กับเอกสาร" api.jquery.com/bind
CodeVirtuoso


3

ขึ้นอยู่กับว่า function1 กำลังทำอะไรอยู่

หาก function1 กำลังทำจาวาสคริปต์แบบง่าย ๆ เช่นการปรับปรุงค่า div หรือบางอย่าง function2 จะเริ่มทำงานหลังจากที่ function1 เสร็จสิ้น

หาก function1 ทำการโทรแบบอะซิงโครนัสเช่นการโทร AJAX คุณจะต้องสร้างวิธีการ "เรียกกลับ" (ajax API ส่วนใหญ่มีพารามิเตอร์ฟังก์ชั่นการโทรกลับ) จากนั้นเรียกใช้ function2 ในการเรียกกลับ เช่น:

function1()
{
  new AjaxCall(ajaxOptions, MyCallback);
}

function MyCallback(result)
{
  function2(result);
}

2

หากต้องดำเนินการตามวิธีที่ 1 หลังจากวิธีที่ 2, 3, 4 ข้อมูลโค้ดต่อไปนี้สามารถเป็นวิธีการแก้ปัญหานี้ได้โดยใช้วัตถุที่เลื่อนออกไปใน JavaScript

function method1(){
  var dfd = new $.Deferred();
     setTimeout(function(){
     console.log("Inside Method - 1"); 
     method2(dfd);	 
    }, 5000);
  return dfd.promise();
}

function method2(dfd){
  setTimeout(function(){
   console.log("Inside Method - 2"); 
   method3(dfd);	
  }, 3000);
}

function method3(dfd){
  setTimeout(function(){
   console.log("Inside Method - 3"); 	
   dfd.resolve();
  }, 3000);
}

function method4(){   
   console.log("Inside Method - 4"); 	
}

var call = method1();

$.when(call).then(function(cb){
  method4();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>


0

หาก function1 เป็นฟังก์ชั่นการซิงค์ที่คุณต้องการเปลี่ยนเป็นอะซิงก์เพราะต้องใช้เวลาพอสมควรและคุณไม่สามารถควบคุมได้เพื่อเพิ่มการโทรกลับ:

function function1 (someVariable) {
    var date = Date.now ();
    while (Date.now () - date < 2000);      // function1 takes some time to complete
    console.log (someVariable);
}
function function2 (someVariable) {
    console.log (someVariable);
}
function onClick () {
    window.setTimeout (() => { function1 ("This is function1"); }, 0);
    window.setTimeout (() => { function2 ("This is function2"); }, 0);
    console.log ("Click handled");  // To show that the function will return before both functions are executed
}
onClick ();

ผลลัพธ์จะเป็น:

Click handled

... และหลังจาก 2 วินาที:

This is function 1
This is function 2

สิ่งนี้ได้ผลเพราะการเรียก window.setTimeout () จะเพิ่มงานลงในงานวนรอบ JS runtine ซึ่งเป็นสิ่งที่ทำให้การโทรแบบ async ทำและเนื่องจากหลักการพื้นฐานของ ไม่เคยถูกขัดจังหวะก่อนที่จะจบ

โปรดสังเกตว่านี่เป็นเรื่องตลกเพราะอาจเป็นรหัสที่เข้าใจยาก ...

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.