Làm cách nào để làm cho jQuery chờ đợi một cuộc gọi Ajax kết thúc trước khi nó trả về?


243

Tôi có một chức năng phía máy chủ yêu cầu đăng nhập. Nếu người dùng đăng nhập, chức năng sẽ trả về 1 khi thành công. Nếu không, chức năng sẽ trả về trang đăng nhập.

Tôi muốn gọi hàm bằng Ajax và jQuery. Những gì tôi làm là gửi yêu cầu với một liên kết thông thường, với chức năng nhấp được áp dụng trên đó. Nếu người dùng không đăng nhập hoặc chức năng không thành công, tôi muốn cuộc gọi Ajax trở về đúng, để kích hoạt href.

Tuy nhiên, khi tôi sử dụng đoạn mã sau, hàm sẽ thoát trước khi cuộc gọi Ajax được thực hiện.

Làm cách nào tôi có thể chuyển hướng người dùng một cách duyên dáng đến trang đăng nhập?

$(".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
Có thể là một chủ đề cũ, nhưng như @kofifus chỉ ra rằng việc đặt async thành false là một thiết kế tồi và "không có thời gian chờ, v.v. sẽ được xử lý". Có thể thử giải pháp đơn giản hóa này - stackoverflow.com/a/11576418/6937841
Shan

Câu trả lời:


355

Nếu bạn không muốn $.ajax()chức năng quay lại ngay lập tức, hãy đặt asynctùy chọn thành 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;
            }
        }
    });
});

Nhưng, tôi sẽ lưu ý rằng điều này sẽ đi ngược lại quan điểm của AJAX. Ngoài ra, bạn nên xử lý các phản hồi trong errorvà các successchức năng. Những chức năng đó sẽ chỉ được gọi khi nhận được phản hồi từ máy chủ.


10
Theo tôi, việc thực hiện tốt các thực hành tốt không phải là đánh giá và là dấu hiệu của một số câu trả lời hay nhất ở đây trên StackOverflow.
semperos

68
Không bao giờ sử dụng async: false. Vòng lặp sự kiện của trình duyệt sẽ bị treo trong khi chờ đợi trên I / O mạng không đáng tin cậy. Luôn luôn có một cách tốt hơn. Trong trường hợp này, mục tiêu của liên kết có thể xác minh phiên người dùng và 302 đến trang đăng nhập.
Matthew

3
Để thử nghiệm, async: falsecó thể rất hữu ích.
Jarrett

4
@Matthew Thật sao? Thay thế cho việc gửi ajax bằng async trước khi gửi người dùng đến trang khác hoặc làm mới là gì?
NoBugs

2
asyncvới giá trị falsekhông còn dùng nữa trong hầu hết các trường hợp sử dụng trình duyệt. Nó có thể ném ngoại lệ trong phiên bản mới hơn của trình duyệt. Xem xhr.spec.whatwg.org/#sync-warning (áp dụng cho asynctham số của openphương thức xhr , đây là những gì sử dụng jQuery).
Frédéric

42

Tôi không sử dụng $.ajaxnhưng các chức năng $.post$.getvì vậy nếu tôi cần chờ phản hồi, tôi sử dụng:

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

34

Đối tượng XMLHttpRequest bên dưới (được jQuery sử dụng để thực hiện yêu cầu) hỗ trợ thuộc tính không đồng bộ. Đặt thành sai . Giống

async: false

24

Thay vì đặt async thành false thường là thiết kế xấu, bạn có thể muốn xem xét việc chặn UI trong khi thao tác đang chờ xử lý.

Điều này có thể đạt được độc đáo với các lời hứa của jQuery như sau:

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

với điều này bây giờ bạn có thể làm:

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

Và giao diện người dùng sẽ chặn cho đến khi lệnh ajax trở lại

xem jsfiddle


Không đặt async thành false làm điều tương tự chính xác ngoại trừ trong một dòng mã?
Vincent

4
Không có gì. Đặt async thành false làm cho async yêu cầu - đó là tạm dừng xử lý cho đến khi nó trả về thường là thông lệ xấu, ví dụ: không có sự kiện, yêu cầu ajax nào khác, thời gian chờ, v.v. Bạn cũng có thể sửa đổi mã ở trên để chỉ chặn một phần của giao diện người dùng trong khi ajax của bạn đang xử lý (tức là phần nó sẽ ảnh hưởng)
kofifus

11

Tôi nghĩ mọi thứ sẽ dễ dàng hơn nếu bạn mã hóa successchức năng của mình để tải trang thích hợp thay vì quay lại truehoặcfalse .

Ví dụ thay vì trả lại, truebạn có thể làm:

window.location="appropriate page";

Theo cách đó, khi chức năng thành công được gọi là trang được chuyển hướng.


5

Trong JS hiện đại, bạn có thể chỉ cần sử dụng async/ await, như:

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

Sau đó gọi nó trong một asyncchức năng như:

let response = await upload();

1
Mã của bạn sẽ không hoạt động ... bạn đang đi đúng hướng nhưng vì nó là như vậy, nó sẽ không hoạt động.
ppetree

Bạn có thể vui lòng giải thích thêm? Sau khi kiểm tra mã tôi đã trả lời câu hỏi này, nó sẽ hoạt động.
Taohidul Hồi giáo

Chờ đợi không thể được sử dụng bên ngoài chức năng async, nó sẽ báo lỗi.
ppetree

Có, tôi đã đề cập đến nó trong dòng cuối cùng thứ hai trong câu trả lời của tôi, "Sau đó gọi nó trong một asyncchức năng".
Đạo Hồi Taohidul

Đó là một giải pháp tuyệt vời nhưng để giúp những người khác tìm kiếm một giải pháp tuyệt vời (như tôi), bạn nên chỉnh sửa mã của mình và hiển thị nó theo cách hoàn chỉnh hơn. Thậm chí có thể bao gồm một liên kết đến một fiddle. Điêu đo thật tuyệt vơi!
ppetree

0

Vì tôi không thấy nó được đề cập ở đây, tôi nghĩ rằng tôi cũng chỉ ra rằng jQuery khi câu lệnh có thể rất hữu ích cho mục đích này.

Ví dụ của họ trông như thế này:

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

Phần "sau đó" sẽ không thực thi cho đến khi phần "khi" kết thúc.


0

Nó sẽ đợi cho đến khi nhận được yêu cầu hoàn thành. Sau đó, tôi sẽ trả lại phần yêu cầu từ chức năng được gọi.

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

Hãy giải thích giải pháp của bạn.
Bỏ học

1
Đã thêm @Dropout.
Hassan Ejaz

Cảm ơn @Hassan Ejaz! Câu trả lời không có lời giải thích và chỉ có mã được gắn cờ là nỗ lực thấp.
Bỏ học
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.