ฉันจะทำให้ jQuery รอให้การโทร Ajax สิ้นสุดก่อนที่จะส่งคืนได้อย่างไร


244

ฉันมีฟังก์ชั่นฝั่งเซิร์ฟเวอร์ที่ต้องเข้าสู่ระบบ หากผู้ใช้เข้าสู่ระบบฟังก์ชั่นจะกลับ 1 เมื่อประสบความสำเร็จ ถ้าไม่ฟังก์ชั่นจะส่งคืนหน้าล็อกอิน

ฉันต้องการเรียกใช้ฟังก์ชันโดยใช้ Ajax และ jQuery สิ่งที่ฉันทำคือส่งคำขอพร้อมลิงก์ปกติโดยมีฟังก์ชั่นคลิกนำไปใช้กับมัน หากผู้ใช้ไม่ได้เข้าสู่ระบบหรือฟังก์ชั่นล้มเหลวฉันต้องการให้ Ajax-call ส่งกลับจริงดังนั้น href ทริกเกอร์

อย่างไรก็ตามเมื่อฉันใช้รหัสต่อไปนี้ฟังก์ชั่นจะออกก่อนที่การโทร Ajax จะเสร็จสิ้น

ฉันจะเปลี่ยนเส้นทางผู้ใช้อย่างสง่างามไปยังหน้าเข้าสู่ระบบได้อย่างไร

$(".my_link").click(
    function(){
    $.ajax({
        url: $(this).attr('href'),
        type: 'GET',
        cache: false,
        timeout: 30000,
        error: function(){
            return true;
        },
        success: function(msg){ 
            if (parseFloat(msg)){
                return false;
            } else {
                return true;
            }
        }
    });
});

2
อาจเป็นเธรดเก่า แต่เนื่องจาก @kofifus ชี้ให้เห็นว่าการตั้งค่า async เป็น false เป็นการออกแบบที่ไม่ดีและ "ไม่มีการหมดเวลา ฯลฯ จะถูกประมวลผล" อาจลองวิธีแก้ปัญหาที่ง่ายกว่านี้ - stackoverflow.com/a/11576418/6937841
Shan

คำตอบ:


357

หากคุณไม่ต้องการให้$.ajax()ฟังก์ชันคืนค่าทันทีให้ตั้งค่าasyncตัวเลือกเป็นfalse:

$(".my_link").click(
    function(){
    $.ajax({
        url: $(this).attr('href'),
        type: 'GET',
        async: false,
        cache: false,
        timeout: 30000,
        error: function(){
            return true;
        },
        success: function(msg){ 
            if (parseFloat(msg)){
                return false;
            } else {
                return true;
            }
        }
    });
});

แต่ฉันจะทราบว่านี่จะเป็นไปตามจุดของ AJAX นอกจากนี้คุณควรจัดการการตอบสนองในerrorและsuccessฟังก์ชั่น ฟังก์ชั่นเหล่านั้นจะถูกเรียกก็ต่อเมื่อได้รับการตอบสนองจากเซิร์ฟเวอร์


10
ในความคิดของฉันการปฏิบัติตามแนวปฏิบัติที่ดีไม่ใช่การตัดสินและเป็นเครื่องหมายของคำตอบที่ดีที่สุดที่นี่ใน StackOverflow
semperos

68
async: falseไม่เคยใช้งาน การวนรอบเหตุการณ์ของเบราว์เซอร์จะหยุดทำงานในขณะที่รอ I / O เครือข่ายที่ไม่น่าเชื่อถือ มีวิธีที่ดีกว่าเสมอ ในกรณีนี้เป้าหมายของลิงก์สามารถยืนยันเซสชันผู้ใช้และ 302 ถึงหน้าล็อกอิน
Matthew

3
สำหรับการทดสอบasync: falseจะมีประโยชน์มาก
Jarrett

4
@ Matthew จริงเหรอ? อะไรคือทางเลือกในการส่ง ajax ด้วย async ก่อนส่งผู้ใช้ไปยังหน้าอื่นหรือรีเฟรช
NoBugs

2
asyncด้วยค่าfalseจะเลิกในกรณีส่วนใหญ่ใช้เบราว์เซอร์ อาจมีข้อยกเว้นในเบราว์เซอร์เวอร์ชันใหม่กว่า ดูxhr.spec.whatwg.org/#sync-warning (ใช้กับasyncพารามิเตอร์ของopenวิธีการxhr ซึ่งเป็นสิ่งที่ใช้ jQuery)
Frédéric

42

ฉันไม่ได้ใช้$.ajaxแต่$.postและ$.getฟังก์ชั่นดังนั้นถ้าฉันต้องรอการตอบสนองที่ผมใช้นี้

$.ajaxSetup({async: false});
$.get("...");

34

วัตถุ XMLHttpRequest พื้นฐาน (ใช้โดย jQuery เพื่อทำการร้องขอ) สนับสนุนคุณสมบัติแบบอะซิงโครนัส ตั้งค่าเป็นเท็จ ชอบ

async: false

24

แทนที่จะตั้งค่า async เป็น false ซึ่งโดยปกติจะเป็นการออกแบบที่ไม่ดีคุณอาจต้องพิจารณาการบล็อก UI ในขณะที่การดำเนินการอยู่ระหว่างดำเนินการ

สิ่งนี้สามารถทำได้โดยสัญญา jQuery ดังนี้:

// same as $.ajax but settings can have a maskUI property
// if settings.maskUI==true, the UI will be blocked while ajax in progress
// if settings.maskUI is other than true, it's value will be used as the color value while bloking (i.e settings.maskUI='rgba(176,176,176,0.7)'
// in addition an hourglass is displayed while ajax in progress
function ajaxMaskUI(settings) {
    function maskPageOn(color) { // color can be ie. 'rgba(176,176,176,0.7)' or 'transparent'
        var div = $('#maskPageDiv');
        if (div.length === 0) {
            $(document.body).append('<div id="maskPageDiv" style="position:fixed;width:100%;height:100%;left:0;top:0;display:none"></div>'); // create it
            div = $('#maskPageDiv');
        }
        if (div.length !== 0) {
            div[0].style.zIndex = 2147483647;
            div[0].style.backgroundColor=color;
            div[0].style.display = 'inline';
        }
    }
    function maskPageOff() {
        var div = $('#maskPageDiv');
        if (div.length !== 0) {
            div[0].style.display = 'none';
            div[0].style.zIndex = 'auto';
        }
    }
    function hourglassOn() {
        if ($('style:contains("html.hourGlass")').length < 1) $('<style>').text('html.hourGlass, html.hourGlass * { cursor: wait !important; }').appendTo('head');
        $('html').addClass('hourGlass');
    }
    function hourglassOff() {
        $('html').removeClass('hourGlass');
    }

    if (settings.maskUI===true) settings.maskUI='transparent';

    if (!!settings.maskUI) {
        maskPageOn(settings.maskUI);
        hourglassOn();
    }

    var dfd = new $.Deferred();
    $.ajax(settings)
        .fail(function(jqXHR, textStatus, errorThrown) {
            if (!!settings.maskUI) {
                maskPageOff();
                hourglassOff();
            }
            dfd.reject(jqXHR, textStatus, errorThrown);
        }).done(function(data, textStatus, jqXHR) {
            if (!!settings.maskUI) {
                maskPageOff();
                hourglassOff();
            }
            dfd.resolve(data, textStatus, jqXHR);
        });

    return dfd.promise();
}

ด้วยสิ่งนี้คุณสามารถทำได้:

ajaxMaskUI({
    url: url,
    maskUI: true // or try for example 'rgba(176,176,176,0.7)'
}).fail(function (jqXHR, textStatus, errorThrown) {
    console.log('error ' + textStatus);
}).done(function (data, textStatus, jqXHR) {
    console.log('success ' + JSON.stringify(data));
});

และ UI จะบล็อกจนกว่าคำสั่ง ajax จะกลับมา

ดูjsfiddle


ไม่ได้ตั้งค่า async ให้เป็นเท็จทำสิ่งเดียวกันยกเว้นในรหัสบรรทัดเดียวหรือไม่
Vincent

4
ไม่ใช่เลย. การตั้งค่า async เป็น false ทำการร้องขอ async - นั่นเป็นการหยุดการประมวลผลจนกว่ามันจะกลับมาซึ่งมักจะเป็นการปฏิบัติที่ไม่ดีเช่นไม่มีเหตุการณ์คำขอ ajax อื่นหมดเวลา ฯลฯ จะถูกประมวลผล นอกจากนี้คุณยังสามารถแก้ไขโค้ดด้านบนเพื่อบล็อกเฉพาะบางส่วนของ UI ในขณะที่
อาแจ็กซ์

11

ผมคิดว่าสิ่งที่จะง่ายขึ้นถ้าคุณรหัสของคุณsuccessฟังก์ชั่นในการโหลดหน้าเว็บที่เหมาะสมแทนที่จะกลับหรือtruefalse

ตัวอย่างเช่นแทนที่จะส่งคืนtrueคุณสามารถทำได้:

window.location="appropriate page";

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


5

ใน JS สมัยใหม่คุณสามารถใช้async/ awaitเช่น:

  async function upload() {
    return new Promise((resolve, reject) => {
        $.ajax({
            url: $(this).attr('href'),
            type: 'GET',
            timeout: 30000,
            success: (response) => {
                resolve(response);
            },
            error: (response) => {
                reject(response);
            }
        })
    })
}

จากนั้นเรียกมันในasyncฟังก์ชั่นเช่น:

let response = await upload();

1
รหัสของคุณไม่ทำงาน ... คุณกำลังถูกทาง แต่มันไม่ทำงาน
ppetree

คุณช่วยอธิบายเพิ่มเติมอีกหน่อยได้ไหม? หลังจากทดสอบรหัสที่ฉันตอบคำถามนี้แล้วมันควรจะใช้ได้
Taohidul Islam

ไม่สามารถใช้งานรออยู่นอกฟังก์ชั่น async เพราะมีข้อผิดพลาดเกิดขึ้น
ppetree

ใช่ฉันพูดถึงมันในบรรทัดสุดท้ายของคำตอบที่สองของฉัน "แล้วเรียกมันในasyncฟังก์ชั่น"
Taohidul Islam

มันเป็นทางออกที่ยอดเยี่ยม แต่เพื่อช่วยให้ผู้อื่นมองหาโซลูชันที่ยอดเยี่ยม (เช่นเดียวกับฉัน) คุณควรแก้ไขโค้ดของคุณและแสดงในแบบที่สมบูรณ์ยิ่งขึ้น บางทีอาจรวมลิงค์ไปยังซอ นั่นจะยอดเยี่ยม!
ppetree

0

เนื่องจากฉันไม่เห็นมันกล่าวถึงที่นี่ฉันคิดว่าฉันยังชี้ให้เห็นว่า jQuery เมื่อคำสั่งจะมีประโยชน์มากสำหรับวัตถุประสงค์นี้

ตัวอย่างของพวกเขามีลักษณะดังนี้:

$.when( $.ajax( "test.aspx" ) ).then(function( data, textStatus, jqXHR ) {
  alert( jqXHR.status ); // Alerts 200
});

ส่วน "จากนั้น" จะไม่ทำงานจนกว่าส่วน "เมื่อ" จะเสร็จสิ้น


0

ควรรอจนกว่าจะได้รับคำขอให้เสร็จสมบูรณ์ หลังจากนั้นฉันจะกลับมาขอเนื้อความจากตำแหน่งที่เรียกใช้ฟังก์ชัน

function foo() {
    var jqXHR = $.ajax({
        url: url,
        type: 'GET',
        async: false,
    });
    return JSON.parse(jqXHR.responseText);  
}

โปรดอธิบายวิธีแก้ปัญหาของคุณ
กลางคัน

1
เพิ่ม @Dropout แล้ว
Hassan Ejaz

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