it-swarm-vi.tech

Làm cách nào để trả về phản hồi từ cuộc gọi không đồng bộ?

Tôi có một hàm foo để thực hiện một yêu cầu Ajax. Làm cách nào tôi có thể trả lại phản hồi từ foo?

Tôi đã thử trả về giá trị từ cuộc gọi lại success cũng như gán phản hồi cho một biến cục bộ bên trong hàm và trả về giá trị đó, nhưng không có cách nào thực sự trả về phản hồi.

function foo() {
    var result;

    $.ajax({
        url: '...',
        success: function(response) {
            result = response;
            // return response; // <- I tried that one as well
        }
    });

    return result;
}

var result = foo(); // It always ends up being `undefined`.
4904
Felix Kling

→ Để có giải thích tổng quát hơn về hành vi không đồng bộ với các ví dụ khác nhau, vui lòng xemTại sao biến của tôi không được thay đổi sau khi tôi sửa đổi nó bên trong hàm? - Tham chiếu mã không đồng bộ

→ Nếu ​​bạn đã hiểu vấn đề, hãy bỏ qua các giải pháp có thể bên dưới.

Vấn đề

Ain Ajax là viết tắt của không đồng bộ . Điều đó có nghĩa là gửi yêu cầu (hay đúng hơn là nhận phản hồi) được đưa ra khỏi luồng thực thi thông thường. Trong ví dụ của bạn, $.ajax trả về ngay lập tức và câu lệnh tiếp theo, return result;, được thực thi trước khi hàm bạn đã chuyển dưới dạng gọi lại success thậm chí được gọi.

Đây là một sự tương tự mà hy vọng làm cho sự khác biệt giữa dòng chảy đồng bộ và không đồng bộ rõ ràng hơn:

Đồng bộ

Hãy tưởng tượng bạn gọi điện thoại cho một người bạn và yêu cầu anh ta tìm kiếm thứ gì đó cho bạn. Mặc dù có thể mất một lúc, bạn đợi điện thoại và nhìn chằm chằm vào không gian, cho đến khi bạn của bạn đưa ra câu trả lời mà bạn cần.

Điều tương tự cũng xảy ra khi bạn thực hiện một cuộc gọi hàm chứa mã "bình thường":

function findItem() {
    var item;
    while(item_not_found) {
        // search
    }
    return item;
}

var item = findItem();

// Do something with item
doSomethingElse();

Mặc dù findItem có thể mất nhiều thời gian để thực thi, bất kỳ mã nào đến sau var item = findItem(); phải chờ cho đến khi hàm trả về kết quả.

Không đồng bộ

Bạn gọi lại cho bạn của bạn vì lý do tương tự. Nhưng lần này bạn nói với anh ấy rằng bạn đang vội và anh ấy nên gọi lại cho bạn trên điện thoại di động của bạn. Bạn cúp máy, rời khỏi nhà và làm bất cứ điều gì bạn dự định làm. Khi bạn của bạn gọi lại cho bạn, bạn đang xử lý thông tin anh ấy đã cung cấp cho bạn.

Đó chính xác là những gì xảy ra khi bạn thực hiện một yêu cầu Ajax.

findItem(function(item) {
    // Do something with item
});
doSomethingElse();

Thay vì chờ phản hồi, việc thực thi tiếp tục ngay lập tức và câu lệnh sau khi lệnh gọi Ajax được thực thi. Để nhận được phản hồi cuối cùng, bạn cung cấp một hàm được gọi sau khi nhận được phản hồi, một gọi lại (nhận thấy điều gì đó? Gọi lại?). Bất kỳ câu lệnh nào đến sau cuộc gọi đó được thực thi trước khi gọi lại.


Các giải pháp)

Nắm bắt bản chất không đồng bộ của JavaScript! Mặc dù các hoạt động không đồng bộ nhất định cung cấp các đối tác đồng bộ ("Ajax"), nhưng thường không khuyến khích sử dụng chúng, đặc biệt là trong bối cảnh trình duyệt.

Tại sao nó xấu khi bạn hỏi?

JavaScript chạy trong luồng UI của trình duyệt và bất kỳ quá trình chạy dài nào cũng sẽ khóa UI, khiến nó không phản hồi. Ngoài ra, có giới hạn trên về thời gian thực hiện đối với JavaScript và trình duyệt sẽ hỏi người dùng có tiếp tục thực hiện hay không.

Tất cả điều này là trải nghiệm người dùng thực sự xấu. Người dùng sẽ không thể biết liệu mọi thứ có hoạt động tốt hay không. Hơn nữa, hiệu quả sẽ tồi tệ hơn đối với người dùng có kết nối chậm.

Sau đây chúng ta sẽ xem xét ba giải pháp khác nhau, tất cả đều được xây dựng chồng lên nhau:

  • Hứa với async/await(ES2017 +, khả dụng trong các trình duyệt cũ hơn nếu bạn sử dụng bộ chuyển đổi hoặc trình tái tạo)
  • Gọi lại (phổ biến trong nút)
  • Hứa hẹn với then()(ES2015 +, khả dụng trong các trình duyệt cũ hơn nếu bạn sử dụng một trong nhiều thư viện hứa hẹn)

Cả ba đều có sẵn trong các trình duyệt hiện tại và nút 7+.


ES2017 +: Hứa hẹn với async/await

Phiên bản ECMAScript được phát hành năm 2017 đã giới thiệu hỗ trợ mức cú pháp cho các hàm không đồng bộ. Với sự trợ giúp của asyncawait, bạn có thể viết không đồng bộ theo "kiểu đồng bộ". Mã vẫn không đồng bộ, nhưng dễ đọc/dễ hiểu hơn.

async/await được xây dựng dựa trên lời hứa: hàm async luôn trả lại lời hứa. await "hủy bỏ" một lời hứa và dẫn đến giá trị của lời hứa đã được giải quyết hoặc ném lỗi nếu lời hứa bị từ chối.

Quan trọng: Bạn chỉ có thể sử dụng await bên trong hàm async. Ngay bây giờ, await cấp cao nhất chưa được hỗ trợ, do đó bạn có thể phải tạo một IIFE async ( Biểu thức hàm được gọi ngay lập tức ) để bắt đầu bối cảnh async.

Bạn có thể đọc thêm về asyncawait trên MDN.

Dưới đây là một ví dụ được xây dựng dựa trên độ trễ trên:

// Using 'superagent' which will return a promise.
var superagent = require('superagent')

// This is isn't declared as `async` because it already returns a promise
function delay() {
  // `delay` returns a promise
  return new Promise(function(resolve, reject) {
    // Only `delay` is able to resolve or reject the promise
    setTimeout(function() {
      resolve(42); // After 3 seconds, resolve the promise with value 42
    }, 3000);
  });
}


async function getAllBooks() {
  try {
    // GET a list of book IDs of the current user
    var bookIDs = await superagent.get('/user/books');
    // wait for 3 seconds (just for the sake of this example)
    await delay();
    // GET information about each book
    return await superagent.get('/books/ids='+JSON.stringify(bookIDs));
  } catch(error) {
    // If any of the awaited promises was rejected, this catch block
    // would catch the rejection reason
    return null;
  }
}

// Start an IIFE to use `await` at the top level
(async function(){
  let books = await getAllBooks();
  console.log(books);
})();

Các phiên bản browsernode hiện tại hỗ trợ async/await. Bạn cũng có thể hỗ trợ các môi trường cũ hơn bằng cách chuyển đổi mã của mình sang ES5 với sự trợ giúp của bộ tái tạo (hoặc các công cụ sử dụng trình tái tạo, chẳng hạn như Babel ).


Để các hàm chấp nhận gọi lại

Một cuộc gọi lại chỉ đơn giản là một chức năng được chuyển đến một chức năng khác. Hàm khác có thể gọi hàm được truyền bất cứ khi nào nó sẵn sàng. Trong ngữ cảnh của một quy trình không đồng bộ, cuộc gọi lại sẽ được gọi bất cứ khi nào quá trình không đồng bộ được thực hiện. Thông thường, kết quả được chuyển đến cuộc gọi lại.

Trong ví dụ về câu hỏi, bạn có thể khiến foo chấp nhận cuộc gọi lại và sử dụng nó như cuộc gọi lại success. Vậy đây

var result = foo();
// Code that depends on 'result'

trở thành

foo(function(result) {
    // Code that depends on 'result'
});

Ở đây chúng tôi đã định nghĩa hàm "nội tuyến" nhưng bạn có thể vượt qua bất kỳ tham chiếu chức năng nào:

function myCallback(result) {
    // Code that depends on 'result'
}

foo(myCallback);

foo chính nó được định nghĩa như sau:

function foo(callback) {
    $.ajax({
        // ...
        success: callback
    });
}

callback sẽ đề cập đến chức năng chúng ta chuyển đến foo khi chúng ta gọi nó và chúng ta chỉ cần chuyển nó cho success. I E. khi yêu cầu Ajax thành công, $.ajax sẽ gọi callback và chuyển phản hồi cho cuộc gọi lại (có thể được gọi bằng result, vì đây là cách chúng tôi xác định cuộc gọi lại).

Bạn cũng có thể xử lý phản hồi trước khi chuyển nó đến cuộc gọi lại:

function foo(callback) {
    $.ajax({
        // ...
        success: function(response) {
            // For example, filter the response
            callback(filtered_response);
        }
    });
}

Viết mã bằng cách gọi lại dễ dàng hơn có vẻ. Rốt cuộc, JavaScript trong trình duyệt chủ yếu dựa vào sự kiện (các sự kiện DOM). Nhận được phản hồi Ajax không gì khác ngoài một sự kiện.
[.__.] Khó khăn có thể phát sinh khi bạn phải làm việc với mã của bên thứ ba, nhưng hầu hết các vấn đề có thể được giải quyết chỉ bằng cách suy nghĩ thông qua dòng ứng dụng.


ES2015 +: Hứa hẹn với then ()

API Promise là một tính năng mới của ECMAScript 6 (ES2015), nhưng nó đã hỗ trợ trình duyệt tốt . Ngoài ra còn có nhiều thư viện triển khai API Promise tiêu chuẩn và cung cấp các phương thức bổ sung để dễ dàng sử dụng và cấu thành các chức năng không đồng bộ (ví dụ: bluebird ).

Lời hứa là các thùng chứa cho các giá trị tương lai. Khi lời hứa nhận được giá trị (đó là đã giải quyết) hoặc khi nó bị hủy (bị từ chối), nó sẽ thông báo cho tất cả "người nghe" muốn truy cập giá trị này.

Ưu điểm so với các cuộc gọi lại đơn giản là chúng cho phép bạn tách mã của bạn và chúng dễ dàng soạn thảo hơn.

Đây là một ví dụ đơn giản về việc sử dụng một lời hứa:

function delay() {
  // `delay` returns a promise
  return new Promise(function(resolve, reject) {
    // Only `delay` is able to resolve or reject the promise
    setTimeout(function() {
      resolve(42); // After 3 seconds, resolve the promise with value 42
    }, 3000);
  });
}

delay()
  .then(function(v) { // `delay` returns a promise
    console.log(v); // Log the value once it is resolved
  })
  .catch(function(v) {
    // Or do something else if it is rejected 
    // (it would not happen in this example, since `reject` is not called).
  });

Áp dụng cho lệnh gọi Ajax của chúng tôi, chúng tôi có thể sử dụng các lời hứa như thế này:

function ajax(url) {
  return new Promise(function(resolve, reject) {
    var xhr = new XMLHttpRequest();
    xhr.onload = function() {
      resolve(this.responseText);
    };
    xhr.onerror = reject;
    xhr.open('GET', url);
    xhr.send();
  });
}

ajax("/echo/json")
  .then(function(result) {
    // Code depending on result
  })
  .catch(function() {
    // An error occurred
  });

Mô tả tất cả các lợi thế mà lời hứa cung cấp nằm ngoài phạm vi của câu trả lời này, nhưng nếu bạn viết mã mới, bạn nên nghiêm túc xem xét chúng. Họ cung cấp một sự trừu tượng hóa và phân tách mã của bạn.

Thông tin thêm về lời hứa: Đá HTML5 - Lời hứa JavaScript

Lưu ý bên lề: Các đối tượng bị trì hoãn của jQuery

Các đối tượng bị trì hoãn là việc thực hiện các lời hứa tùy chỉnh của jQuery (trước khi API Promise được chuẩn hóa). Họ hành xử gần giống như những lời hứa nhưng để lộ một API hơi khác.

Mọi phương thức jQuery của jQuery đã trả về một "đối tượng hoãn lại" (thực ra là một lời hứa về một đối tượng bị trì hoãn) mà bạn có thể trả về từ hàm của mình:

function ajax() {
    return $.ajax(...);
}

ajax().done(function(result) {
    // Code depending on result
}).fail(function() {
    // An error occurred
});

Lưu ý bên lề: Promise gotchas

Hãy nhớ rằng các lời hứa và các đối tượng bị trì hoãn chỉ là container cho một giá trị trong tương lai, chúng không phải là giá trị của chính nó. Ví dụ: giả sử bạn có những điều sau đây:

function checkPassword() {
    return $.ajax({
        url: '/password',
        data: {
            username: $('#username').val(),
            password: $('#password').val()
        },
        type: 'POST',
        dataType: 'json'
    });
}

if (checkPassword()) {
    // Tell the user they're logged in
}

Mã này hiểu sai các vấn đề không đồng bộ ở trên. Cụ thể, $.ajax() không đóng băng mã trong khi nó kiểm tra trang '/ password' trên máy chủ của bạn - nó sẽ gửi yêu cầu đến máy chủ và trong khi chờ, ngay lập tức trả về một đối tượng Trì hoãn Ajax, không phải phản hồi từ máy chủ. Điều đó có nghĩa là câu lệnh if sẽ luôn lấy đối tượng Trì hoãn này, coi nó là true và tiếp tục như thể người dùng đã đăng nhập. Không tốt.

Nhưng cách khắc phục rất dễ:

checkPassword()
.done(function(r) {
    if (r) {
        // Tell the user they're logged in
    } else {
        // Tell the user their password was bad
    }
})
.fail(function(x) {
    // Tell the user something bad happened
});

Không được đề xuất: Các cuộc gọi "Ajax" đồng bộ

Như tôi đã đề cập, một số hoạt động không đồng bộ (!) Có các đối tác đồng bộ. Tôi không ủng hộ việc sử dụng chúng, nhưng để hoàn thiện, đây là cách bạn sẽ thực hiện một cuộc gọi đồng bộ:

Không có jQuery

Nếu bạn trực tiếp sử dụng một đối tượng XMLHTTPRequest , hãy chuyển false làm đối số thứ ba cho .open .

jQuery

Nếu bạn sử dụng jQuery , bạn có thể đặt tùy chọn async thành false. Lưu ý rằng tùy chọn này là không dùng nữa kể từ jQuery 1.8. Sau đó, bạn vẫn có thể sử dụng gọi lại success hoặc truy cập thuộc tính responseText của đối tượng jqXHR :

function foo() {
    var jqXHR = $.ajax({
        //...
        async: false
    });
    return jqXHR.responseText;
}

Nếu bạn sử dụng bất kỳ phương thức jQuery Ajax nào khác, chẳng hạn như $.get, $.getJSON, v.v., bạn phải thay đổi nó thành $.ajax (vì bạn chỉ có thể chuyển tham số cấu hình thành $.ajax).

Đứng lên! Không thể thực hiện yêu cầu JSONP đồng bộ. JSONP về bản chất luôn luôn không đồng bộ (một lý do nữa để thậm chí không xem xét tùy chọn này).

5203
Felix Kling

Nếu bạn không sử dụng jQuery trong mã của bạn, câu trả lời này là dành cho bạn

Mã của bạn phải là một cái gì đó dọc theo dòng này:

function foo() {
    var httpRequest = new XMLHttpRequest();
    httpRequest.open('GET', "/echo/json");
    httpRequest.send();
    return httpRequest.responseText;
}

var result = foo(); // always ends up being 'undefined'

Felix Kling đã làm rất tốt khi viết câu trả lời cho những người sử dụng jQuery cho AJAX, tôi đã quyết định cung cấp một giải pháp thay thế cho những người không biết.

( Lưu ý, đối với những người sử dụng API fetch mới, Angular hoặc những lời hứa tôi đã thêm một câu trả lời khác bên dưới )


Những gì bạn đang đối mặt

Đây là một bản tóm tắt ngắn gọn về "Giải thích vấn đề" từ câu trả lời khác, nếu bạn không chắc chắn sau khi đọc nó, hãy đọc nó.

Ain AJAX là viết tắt của không đồng bộ . Điều đó có nghĩa là gửi yêu cầu (hay đúng hơn là nhận phản hồi) được đưa ra khỏi luồng thực thi thông thường. Trong ví dụ của bạn, .send trả về ngay lập tức và câu lệnh tiếp theo, return result;, được thực thi trước khi hàm bạn chuyển qua khi gọi lại success thậm chí được gọi.

Điều này có nghĩa là khi bạn quay trở lại, trình nghe bạn đã xác định chưa thực thi, điều đó có nghĩa là giá trị bạn trả về chưa được xác định.

Đây là một tương tự đơn giản

function getFive(){ 
    var a;
    setTimeout(function(){
         a=5;
    },10);
    return a;
}

(Vĩ cầm)

Giá trị của a được trả về là undefined do phần a=5 chưa được thực thi. AJAX hoạt động như thế này, bạn đang trả lại giá trị trước khi máy chủ có cơ hội cho trình duyệt của bạn biết giá trị đó là gì.

Một giải pháp khả thi cho vấn đề này là mã tích cực lại, cho chương trình của bạn biết phải làm gì khi tính toán hoàn tất.

function onComplete(a){ // When the code completes, do this
    alert(a);
}

function getFive(whenDone){ 
    var a;
    setTimeout(function(){
         a=5;
         whenDone(a);
    },10);
}

Điều này được gọi là CPS . Về cơ bản, chúng tôi sẽ chuyển getFive một hành động để thực hiện khi nó hoàn thành, chúng tôi đang nói cho mã của mình cách phản ứng khi một sự kiện hoàn thành (như cuộc gọi AJAX của chúng tôi hoặc trong trường hợp này là thời gian chờ).

Cách sử dụng sẽ là:

getFive(onComplete);

Mà nên cảnh báo "5" cho màn hình. (Vĩ cầm) .

Phương pháp khả thi

Về cơ bản có hai cách để giải quyết điều này:

  1. Thực hiện cuộc gọi AJAX đồng bộ (hãy gọi nó là SJAX).
  2. Cấu trúc lại mã của bạn để hoạt động đúng với các cuộc gọi lại.

1. Đồng bộ AJAX - Đừng làm điều đó !!

Đối với AJAX đồng bộ, đừng làm điều đó! Câu trả lời của Felix nêu lên một số lập luận thuyết phục về lý do tại sao đó là một ý tưởng tồi. Để tổng hợp, nó sẽ đóng băng trình duyệt của người dùng cho đến khi máy chủ trả lời phản hồi và tạo ra trải nghiệm người dùng rất tệ. Đây là một bản tóm tắt ngắn khác được lấy từ MDN về lý do:

XMLHttpRequest hỗ trợ cả truyền thông đồng bộ và không đồng bộ. Tuy nhiên, nói chung, các yêu cầu không đồng bộ nên được ưu tiên hơn các yêu cầu đồng bộ vì lý do hiệu suất.

Nói tóm lại, các yêu cầu đồng bộ chặn việc thực thi mã ... ... điều này có thể gây ra sự cố nghiêm trọng ...

Nếu bạn để làm điều đó, bạn có thể truyền cờ: Đây là cách:

var request = new XMLHttpRequest();
request.open('GET', 'yourURL', false);  // `false` makes the request synchronous
request.send(null);

if (request.status === 200) {// That's HTTP for 'ok'
  console.log(request.responseText);
}

2. Tái cấu trúc mã

Hãy để chức năng của bạn chấp nhận một cuộc gọi lại. Trong mã ví dụ foo có thể được thực hiện để chấp nhận gọi lại. Chúng tôi sẽ nói cho mã của chúng tôi cách phản ứng khi foo hoàn thành.

Vì thế:

var result = foo();
// code that depends on `result` goes here

Trở thành:

foo(function(result) {
    // code that depends on `result`
});

Ở đây chúng ta đã truyền một hàm ẩn danh, nhưng chúng ta có thể dễ dàng chuyển một tham chiếu đến một hàm hiện có, làm cho nó trông giống như:

function myHandler(result) {
    // code that depends on `result`
}
foo(myHandler);

Để biết thêm chi tiết về cách thức thiết kế gọi lại này được thực hiện, hãy kiểm tra câu trả lời của Felix.

Bây giờ, hãy xác định foo chính nó để hành động phù hợp

function foo(callback) {
    var httpRequest = new XMLHttpRequest();
    httpRequest.onload = function(){ // when the request is loaded
       callback(httpRequest.responseText);// we're calling our method
    };
    httpRequest.open('GET', "/echo/json");
    httpRequest.send();
}

(vĩ cầm)

Bây giờ chúng tôi đã làm cho hàm foo của chúng tôi chấp nhận một hành động để chạy khi AJAX hoàn thành thành công, chúng tôi có thể mở rộng điều này hơn nữa bằng cách kiểm tra xem trạng thái phản hồi không phải là 200 và hành động tương ứng (tạo trình xử lý lỗi và như vậy). Giải quyết hiệu quả vấn đề của chúng tôi.

Nếu bạn vẫn gặp khó khăn trong việc hiểu điều này hãy đọc AJAX hướng dẫn bắt đầu tại MDN.

1002
Benjamin Gruenbaum

XMLHttpRequest 2 (trước hết hãy đọc câu trả lời từ Benjamin Gruenbaum & Felix Kling )

Nếu bạn không sử dụng jQuery và muốn một XMLHttpRequest 2 ngắn đẹp hoạt động trên các trình duyệt hiện đại và cả trên các trình duyệt di động, tôi khuyên bạn nên sử dụng nó theo cách này:

function ajax(a, b, c){ // URL, callback, just a placeholder
  c = new XMLHttpRequest;
  c.open('GET', a);
  c.onload = b;
  c.send()
}

Bạn có thể thấy:

  1. Nó ngắn hơn tất cả các chức năng khác được liệt kê.
  2. Cuộc gọi lại được đặt trực tiếp (vì vậy không có thêm các lần đóng không cần thiết).
  3. Nó sử dụng tải mới (vì vậy bạn không phải kiểm tra trạng thái sẵn sàng &&)
  4. Có một số tình huống khác mà tôi không nhớ làm cho XMLHttpRequest 1 gây khó chịu.

Có hai cách để nhận được phản hồi của lệnh gọi Ajax này (ba cách sử dụng tên var XMLHttpRequest):

Điều đơn giản nhất:

this.response

Hoặc nếu vì một lý do nào đó, bạn bind() gọi lại cho một lớp:

e.target.response

Thí dụ:

function callback(e){
  console.log(this.response);
}
ajax('URL', callback);

Hoặc (một trong những chức năng ẩn danh tốt hơn luôn là một vấn đề):

ajax('URL', function(e){console.log(this.response)});

Không có gì dễ dàng hơn.

Bây giờ một số người có thể sẽ nói rằng tốt hơn là sử dụng onreadystatechange hoặc thậm chí tên biến XMLHttpRequest. Sai rồi.

Hãy xem Các tính năng nâng cao của XMLHttpRequest

Nó hỗ trợ tất cả * trình duyệt hiện đại. Và tôi có thể xác nhận rằng tôi đang sử dụng cách tiếp cận này vì XMLHttpRequest 2 tồn tại. Tôi chưa bao giờ có bất kỳ loại vấn đề nào trên tất cả các trình duyệt tôi sử dụng.

onreadystatechange chỉ hữu ích nếu bạn muốn có được các tiêu đề ở trạng thái 2.

Sử dụng tên biến XMLHttpRequest là một lỗi lớn khác khi bạn cần thực hiện cuộc gọi lại bên trong việc đóng onload/oreadystatechange nếu không bạn đã mất nó.


Bây giờ nếu bạn muốn một cái gì đó phức tạp hơn bằng cách sử dụng bài đăng và FormData, bạn có thể dễ dàng mở rộng chức năng này:

function x(a, b, e, d, c){ // URL, callback, method, formdata or {key:val},placeholder
  c = new XMLHttpRequest;
  c.open(e||'get', a);
  c.onload = b;
  c.send(d||null)
}

Một lần nữa ... đó là một chức năng rất ngắn, nhưng nó có được & đăng.

Ví dụ về cách sử dụng:

x(url, callback); // By default it's get so no need to set
x(url, callback, 'post', {'key': 'val'}); // No need to set post data

Hoặc vượt qua một yếu tố hình thức đầy đủ (document.getElementsByTagName('form')[0]):

var fd = new FormData(form);
x(url, callback, 'post', fd);

Hoặc đặt một số giá trị tùy chỉnh:

var fd = new FormData();
fd.append('key', 'val')
x(url, callback, 'post', fd);

Như bạn có thể thấy tôi đã không thực hiện đồng bộ hóa ... đó là một điều tồi tệ.

Đã nói rằng ... tại sao không làm điều đó một cách dễ dàng?


Như đã đề cập trong bình luận, việc sử dụng lỗi && đồng bộ hoàn toàn phá vỡ điểm của câu trả lời. Đó là một cách ngắn gọn đẹp để sử dụng Ajax theo cách thích hợp?

Trình xử lý lỗi

function x(a, b, e, d, c){ // URL, callback, method, formdata or {key:val}, placeholder
  c = new XMLHttpRequest;
  c.open(e||'get', a);
  c.onload = b;
  c.onerror = error;
  c.send(d||null)
}

function error(e){
  console.log('--Error--', this.type);
  console.log('this: ', this);
  console.log('Event: ', e)
}
function displayAjax(e){
  console.log(e, this);
}
x('WRONGURL', displayAjax);

Trong đoạn script trên, bạn có một trình xử lý lỗi được xác định tĩnh để nó không ảnh hưởng đến chức năng. Trình xử lý lỗi cũng có thể được sử dụng cho các chức năng khác.

Nhưng để thực sự nhận ra một lỗi, only way là viết một URL sai trong trường hợp mọi trình duyệt đều đưa ra một lỗi.

Trình xử lý lỗi có thể hữu ích nếu bạn đặt tiêu đề tùy chỉnh, đặt answerType thành bộ đệm mảng blob hoặc bất cứ điều gì ...

Ngay cả khi bạn vượt qua 'POSTAPAPAP' như phương thức, nó sẽ không gây ra lỗi.

Ngay cả khi bạn vượt qua 'fdggdgilfdghfldj' dưới dạng formdata, nó sẽ không gây ra lỗi.

Trong trường hợp đầu tiên, lỗi nằm trong displayAjax() dưới this.statusTextMethod not Allowed.

Trong trường hợp thứ hai, nó chỉ đơn giản hoạt động. Bạn phải kiểm tra ở phía máy chủ nếu bạn chuyển đúng dữ liệu bài đăng.

tên miền chéo không được phép ném lỗi tự động.

Trong phản hồi lỗi, không có mã lỗi.

Chỉ có this.type được đặt thành lỗi.

Tại sao thêm một trình xử lý lỗi nếu bạn hoàn toàn không kiểm soát được lỗi? Hầu hết các lỗi được trả lại bên trong này trong hàm gọi lại displayAjax().

Vì vậy: Không cần kiểm tra lỗi nếu bạn có thể sao chép và dán URL đúng cách. ;)

PS: Là thử nghiệm đầu tiên tôi đã viết x ('x', displayAjax) ... và nó hoàn toàn có phản hồi ... ??? Vì vậy, tôi đã kiểm tra thư mục chứa HTML và có một tệp có tên 'x.xml'. Vì vậy, ngay cả khi bạn quên phần mở rộng của tệp XMLHttpRequest 2 SILL TÌM NÓ. Tôi LOL


Đọc tệp đồng bộ

Đừng làm vậy.

Nếu bạn muốn chặn trình duyệt trong một thời gian, hãy tải đồng bộ tệp .txt lớn.

function omg(a, c){ // URL
  c = new XMLHttpRequest;
  c.open('GET', a, true);
  c.send();
  return c; // Or c.response
}

Bây giờ bạn có thể làm

 var res = omg('thisIsGonnaBlockThePage.txt');

Không có cách nào khác để làm điều này theo cách không đồng bộ. (Vâng, với vòng lặp setTimeout ... nhưng nghiêm túc chứ?)

Một điểm khác là ... nếu bạn làm việc với API hoặc chỉ các tệp trong danh sách của riêng bạn hoặc bất cứ điều gì bạn luôn sử dụng các chức năng khác nhau cho mỗi yêu cầu ...

Chỉ khi bạn có một trang nơi bạn tải luôn cùng một XML/JSON hoặc bất cứ thứ gì bạn chỉ cần một hàm. Trong trường hợp đó, hãy sửa đổi một chút hàm Ajax và thay thế b bằng hàm đặc biệt của bạn.


Các chức năng trên là để sử dụng cơ bản.

Nếu bạn muốn loại bỏ chức năng ...

Vâng, bạn có thể.

Tôi đang sử dụng rất nhiều API và một trong những hàm đầu tiên tôi tích hợp vào mọi trang HTML là hàm Ajax đầu tiên trong câu trả lời này, chỉ với GET ...

Nhưng bạn có thể làm rất nhiều thứ với XMLHttpRequest 2:

Tôi đã tạo một trình quản lý tải xuống (sử dụng các phạm vi ở cả hai phía với sơ yếu lý lịch, trình quay phim, hệ thống tập tin), các trình chuyển đổi hình ảnh khác nhau bằng cách sử dụng canvas, điền vào cơ sở dữ liệu SQL web với các cơ sở 64 và nhiều hơn nữa ... Nhưng trong những trường hợp này, bạn chỉ nên tạo một hàm mục đích ... đôi khi bạn cần một blob, bộ đệm mảng, bạn có thể đặt tiêu đề, ghi đè mimetype và có nhiều hơn nữa ...

Nhưng câu hỏi ở đây là làm thế nào để trả về phản hồi Ajax ... (Tôi đã thêm một cách dễ dàng.)

363
cocco

Nếu bạn đang sử dụng lời hứa, câu trả lời này là dành cho bạn.

Điều này có nghĩa là AngularJS, jQuery (đã hoãn lại), thay thế XHR gốc (tìm nạp), EmberJS, lưu BackboneJS hoặc bất kỳ thư viện nút nào trả về lời hứa.

Mã của bạn phải là một cái gì đó dọc theo dòng này:

function foo() {
    var data;
    // or $.get(...).then, or request(...).then, or query(...).then
    fetch("/echo/json").then(function(response){
        data = response.json();
    });
    return data;
}

var result = foo(); // result is always undefined no matter what.

Felix Kling đã làm rất tốt khi viết câu trả lời cho những người sử dụng jQuery với các cuộc gọi lại cho AJAX. Tôi có một câu trả lời cho XHR bản địa. Câu trả lời này dành cho việc sử dụng chung các lời hứa hoặc ở mặt trước hoặc phụ trợ.


Vấn đề cốt lõi

Mô hình đồng thời JavaScript trong trình duyệt và trên máy chủ có NodeJS/io.js là không đồng bộphản ứng.

Bất cứ khi nào bạn gọi một phương thức trả về một lời hứa, các trình xử lý thenluôn luôn được thực thi không đồng bộ - nghĩa là, after mã bên dưới chúng không nằm trong trình xử lý .then.

Điều này có nghĩa là khi bạn trả về data trình xử lý then mà bạn đã xác định chưa thực thi. Đến lượt điều này có nghĩa là giá trị bạn trả về chưa được đặt thành giá trị chính xác theo thời gian.

Đây là một tương tự đơn giản cho vấn đề:

    function getFive(){
        var data;
        setTimeout(function(){ // set a timer for one second in the future
           data = 5; // after a second, do this
        }, 1000);
        return data;
    }
    document.body.innerHTML = getFive(); // `undefined` here and not 5

Giá trị của dataundefined do phần data = 5 chưa được thực thi. Nó có thể sẽ thực thi trong một giây nhưng vào thời điểm đó nó không liên quan đến giá trị được trả về.

Vì hoạt động chưa xảy ra (AJAX, cuộc gọi máy chủ, IO, bộ đếm thời gian) bạn sẽ trả lại giá trị trước khi yêu cầu có cơ hội cho mã của bạn biết giá trị đó là gì.

Một giải pháp khả thi cho vấn đề này là mã tích cực lại, cho chương trình của bạn biết phải làm gì khi tính toán hoàn tất. Hứa chủ động kích hoạt điều này bằng cách tạm thời (nhạy cảm với thời gian) trong tự nhiên.

Tóm tắt nhanh về những lời hứa

Lời hứa là một giá trị theo thời gian. Hứa có trạng thái, chúng bắt đầu như đang chờ xử lý mà không có giá trị và có thể giải quyết:

  • hoàn thành có nghĩa là tính toán hoàn thành thành công.
  • bị từ chối có nghĩa là tính toán thất bại.

Một lời hứa chỉ có thể thay đổi trạng thái một lần sau đó nó sẽ luôn ở cùng một trạng thái mãi mãi. Bạn có thể đính kèm các trình xử lý then để hứa sẽ trích xuất giá trị của chúng và xử lý các lỗi. then xử lý cho phép xâu chuỗi của các cuộc gọi. Lời hứa được tạo bởi sử dụng API trả về chúng . Ví dụ, các lời hứa trả lại AJAX fetch hoặc jQuery's $.get hiện đại hơn.

Khi chúng tôi gọi .then trên một lời hứa và trả lại một cái gì đó từ nó - chúng tôi nhận được một lời hứa cho giá trị được xử lý. Nếu chúng ta trả lại một lời hứa khác, chúng ta sẽ nhận được những điều tuyệt vời, nhưng hãy giữ ngựa của chúng ta.

Với lời hứa

Chúng ta hãy xem làm thế nào chúng ta có thể giải quyết vấn đề trên với những lời hứa. Trước tiên, hãy chứng minh sự hiểu biết của chúng tôi về các trạng thái hứa từ trên bằng cách sử dụng Hàm tạo hứa hẹn để tạo hàm trì hoãn:

function delay(ms){ // takes amount of milliseconds
    // returns a new promise
    return new Promise(function(resolve, reject){
        setTimeout(function(){ // when the time is up
            resolve(); // change the promise to the fulfilled state
        }, ms);
    });
}

Bây giờ, sau khi chúng tôi chuyển đổi setTimeout để sử dụng lời hứa, chúng tôi có thể sử dụng then để làm cho nó được tính:

function delay(ms){ // takes amount of milliseconds
  // returns a new promise
  return new Promise(function(resolve, reject){
    setTimeout(function(){ // when the time is up
      resolve(); // change the promise to the fulfilled state
    }, ms);
  });
}

function getFive(){
  // we're RETURNING the promise, remember, a promise is a wrapper over our value
  return delay(100).then(function(){ // when the promise is ready
      return 5; // return the value 5, promises are all about return values
  })
}
// we _have_ to wrap it like this in the call site, we can't access the plain value
getFive().then(function(five){ 
   document.body.innerHTML = five;
});

Về cơ bản, thay vì trả về một giá trị mà chúng ta không thể làm được do mô hình tương tranh - chúng ta sẽ trả về một {trình bao bọc cho một giá trị mà chúng ta có thể unsrap với then. Nó giống như một hộp bạn có thể mở bằng then.

Áp dụng điều này

Điều này tương tự với lệnh gọi API ban đầu của bạn, bạn có thể:

function foo() {
    // RETURN the promise
    return fetch("/echo/json").then(function(response){
        return response.json(); // process it inside the `then`
    });
}

foo().then(function(response){
    // access the value inside the `then`
})

Vì vậy, điều này làm việc như là tốt. Chúng tôi đã học được rằng chúng tôi không thể trả về các giá trị từ các cuộc gọi đã không đồng bộ nhưng chúng tôi có thể sử dụng các lời hứa và xâu chuỗi chúng để thực hiện xử lý. Bây giờ chúng ta biết làm thế nào để trả về phản hồi từ một cuộc gọi không đồng bộ.

ES2015 (ES6)

ES6 giới thiệu máy phát điện là các chức năng có thể quay lại ở giữa và sau đó tiếp tục điểm mà chúng đã ở. Điều này thường hữu ích cho các chuỗi, ví dụ:

function* foo(){ // notice the star, this is ES6 so new browsers/node/io only
    yield 1;
    yield 2;
    while(true) yield 3;
}

Là một hàm trả về một iterator qua chuỗi 1,2,3,3,3,3,.... có thể được lặp lại. Mặc dù điều này là thú vị của riêng nó và mở ra nhiều khả năng có một trường hợp đặc biệt thú vị.

Nếu chuỗi chúng tôi tạo ra là một chuỗi các hành động chứ không phải là số - chúng tôi có thể tạm dừng chức năng bất cứ khi nào một hành động được thực hiện và chờ đợi nó trước khi chúng tôi tiếp tục chức năng. Vì vậy, thay vì một chuỗi các số, chúng ta cần một chuỗi các giá trị tương lai - đó là: lời hứa.

Thủ thuật có phần phức tạp nhưng rất mạnh mẽ này cho phép chúng ta viết mã không đồng bộ một cách đồng bộ. Có một số "vận động viên" làm điều này cho bạn, viết một là một vài dòng mã ngắn nhưng nằm ngoài phạm vi của câu trả lời này. Tôi sẽ sử dụng Promise.coroutine của Bluebird tại đây, nhưng có các trình bao bọc khác như co hoặc Q.async.

var foo = coroutine(function*(){
    var data = yield fetch("/echo/json"); // notice the yield
    // code here only executes _after_ the request is done
    return data.json(); // data is defined
});

Phương pháp này trả về một lời hứa mà chúng ta có thể tiêu thụ từ các coroutine khác. Ví dụ:

var main = coroutine(function*(){
   var bar = yield foo(); // wait our earlier coroutine, it returns a promise
   // server call done here, code below executes when done
   var baz = yield fetch("/api/users/"+bar.userid); // depends on foo's result
   console.log(baz); // runs after both requests done
});
main();

ES2016 (ES7)

Trong ES7, điều này được chuẩn hóa hơn nữa, có một số đề xuất ngay bây giờ nhưng trong tất cả chúng, bạn có thể await hứa. Đây chỉ là "đường" (cú pháp đẹp hơn) cho đề xuất ES6 ở trên bằng cách thêm các từ khóa asyncawait. Làm ví dụ trên:

async function foo(){
    var data = await fetch("/echo/json"); // notice the await
    // code here only executes _after_ the request is done
    return data.json(); // data is defined
}

Nó vẫn trả lại một lời hứa giống nhau :)

292
Benjamin Gruenbaum

Bạn đang sử dụng Ajax không chính xác. Ý tưởng là không để nó trả lại bất cứ thứ gì, mà thay vào đó, chuyển dữ liệu sang một thứ gọi là chức năng gọi lại, xử lý dữ liệu.

Đó là:

function handleData( responseData ) {

    // Do what you want with the data
    console.log(responseData);
}

$.ajax({
    url: "hi.php",
    ...
    success: function ( data, status, XHR ) {
        handleData(data);
    }
});

Trả lại bất cứ điều gì trong trình xử lý trình sẽ không làm gì cả. Thay vào đó, bạn phải xử lý dữ liệu hoặc thực hiện những gì bạn muốn với dữ liệu trực tiếp bên trong chức năng thành công.

230
Nic

Giải pháp đơn giản nhất là tạo một hàm JavaScript và gọi nó cho cuộc gọi lại Ajax success.

function callServerAsync(){
    $.ajax({
        url: '...',
        success: function(response) {

            successCallback(response);
        }
    });
}

function successCallback(responseObj){
    // Do something like read the response and show data
    alert(JSON.stringify(responseObj)); // Only applicable to JSON response
}

function foo(callback) {

    $.ajax({
        url: '...',
        success: function(response) {
           return callback(null, response);
        }
    });
}

var result = foo(function(err, result){
          if (!err)
           console.log(result);    
}); 
219
Hemant Bavle

Tôi sẽ trả lời với một truyện tranh vẽ tay kinh khủng. Hình ảnh thứ hai là lý do tại sao resultundefined trong ví dụ mã của bạn.

 enter image description here

195
Johannes Fahrenkrug

Góc1

Đối với những người đang sử dụng AngularJS , có thể xử lý tình huống này bằng cách sử dụng Promises.

Ở đây nó nói,

Lời hứa có thể được sử dụng để hủy bỏ các chức năng không đồng bộ và cho phép một chuỗi nhiều chức năng lại với nhau.

Bạn có thể tìm thấy một lời giải thích tốt đẹp ở đây cũng.

Ví dụ được tìm thấy trong docs được đề cập dưới đây.

  promiseB = promiseA.then(
    function onSuccess(result) {
      return result + 1;
    }
    ,function onError(err) {
      //Handle error
    }
  );

 // promiseB will be resolved immediately after promiseA is resolved 
 // and its value will be the result of promiseA incremented by 1.

Angular2 và sau đó

Trong Angular2 hãy xem ví dụ sau, nhưng được khuyến nghị để sử dụng Observables với Angular2.

 search(term: string) {
     return this.http
  .get(`https://api.spotify.com/v1/search?q=${term}&type=artist`)
  .map((response) => response.json())
  .toPromise();

}

Bạn có thể tiêu thụ theo cách này,

search() {
    this.searchService.search(this.searchField.value)
      .then((result) => {
    this.result = result.artists.items;
  })
  .catch((error) => console.error(error));
}

Xem bản gốc bài ở đây. Nhưng TypeScript không hỗ trợ Prom6 es6 gốc , nếu bạn muốn sử dụng nó, bạn có thể cần plugin cho điều đó.

Ngoài ra, đây là những lời hứa spec xác định ở đây.

143
Maleen Abewardana

Hầu hết các câu trả lời ở đây đều đưa ra các đề xuất hữu ích khi bạn có một thao tác async duy nhất, nhưng đôi khi, điều này xuất hiện khi bạn cần thực hiện một thao tác không đồng bộ cho mỗi mục trong một mảng hoặc cấu trúc giống như danh sách khác. Sự cám dỗ là để làm điều này:

// WRONG
var results = [];
theArray.forEach(function(entry) {
    doSomethingAsync(entry, function(result) {
        results.Push(result);
    });
});
console.log(results); // E.g., using them, returning them, etc.

Thí dụ:

// WRONG
var theArray = [1, 2, 3];
var results = [];
theArray.forEach(function(entry) {
    doSomethingAsync(entry, function(result) {
        results.Push(result);
    });
});
console.log("Results:", results); // E.g., using them, returning them, etc.

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
  max-height: 100% !important;
}

Lý do không hoạt động là các cuộc gọi lại từ doSomethingAsync vẫn chưa chạy khi bạn đang cố gắng sử dụng kết quả.

Vì vậy, nếu bạn có một mảng (hoặc danh sách một số loại) và muốn thực hiện các thao tác không đồng bộ cho mỗi mục, bạn có hai tùy chọn: Thực hiện các thao tác song song (chồng chéo) hoặc nối tiếp (lần lượt theo thứ tự).

Song song, tương đông

Bạn có thể bắt đầu tất cả chúng và theo dõi xem có bao nhiêu cuộc gọi lại mà bạn mong đợi và sau đó sử dụng kết quả khi bạn nhận được nhiều cuộc gọi lại:

var results = [];
var expecting = theArray.length;
theArray.forEach(function(entry, index) {
    doSomethingAsync(entry, function(result) {
        results[index] = result;
        if (--expecting === 0) {
            // Done!
            console.log("Results:", results); // E.g., using the results
        }
    });
});

Thí dụ:

var theArray = [1, 2, 3];
var results = [];
var expecting = theArray.length;
theArray.forEach(function(entry, index) {
    doSomethingAsync(entry, function(result) {
        results[index] = result;
        if (--expecting === 0) {
            // Done!
            console.log("Results:", results); // E.g., using the results
        }
    });
});

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
  max-height: 100% !important;
}

(Chúng tôi có thể loại bỏ expecting và chỉ sử dụng results.length === theArray.length, nhưng điều đó khiến chúng tôi mở ra khả năng theArray bị thay đổi trong khi các cuộc gọi vẫn còn tồn tại ...)

Lưu ý cách chúng tôi sử dụng index từ forEach để lưu kết quả trong results ở cùng vị trí với mục mà nó liên quan đến, ngay cả khi kết quả không theo thứ tự (vì các cuộc gọi async không nhất thiết phải hoàn thành theo thứ tự bắt đầu ).

Nhưng nếu bạn cần return những kết quả đó từ một hàm thì sao? Như các câu trả lời khác đã chỉ ra, bạn không thể; bạn phải chấp nhận chức năng của mình và gọi lại (hoặc trả lại Promise ). Đây là phiên bản gọi lại:

function doSomethingWith(theArray, callback) {
    var results = [];
    var expecting = theArray.length;
    theArray.forEach(function(entry, index) {
        doSomethingAsync(entry, function(result) {
            results[index] = result;
            if (--expecting === 0) {
                // Done!
                callback(results);
            }
        });
    });
}
doSomethingWith(theArray, function(results) {
    console.log("Results:", results);
});

Thí dụ:

function doSomethingWith(theArray, callback) {
    var results = [];
    var expecting = theArray.length;
    theArray.forEach(function(entry, index) {
        doSomethingAsync(entry, function(result) {
            results[index] = result;
            if (--expecting === 0) {
                // Done!
                callback(results);
            }
        });
    });
}
doSomethingWith([1, 2, 3], function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
  max-height: 100% !important;
}

Hoặc đây là phiên bản trả về Promise thay vào đó:

function doSomethingWith(theArray) {
    return new Promise(function(resolve) {
        var results = [];
        var expecting = theArray.length;
        theArray.forEach(function(entry, index) {
            doSomethingAsync(entry, function(result) {
                results[index] = result;
                if (--expecting === 0) {
                    // Done!
                    resolve(results);
                }
            });
        });
    });
}
doSomethingWith(theArray).then(function(results) {
    console.log("Results:", results);
});

Tất nhiên, nếu doSomethingAsync vượt qua lỗi của chúng tôi, chúng tôi sẽ sử dụng reject để từ chối lời hứa khi chúng tôi gặp lỗi.)

Thí dụ:

function doSomethingWith(theArray) {
    return new Promise(function(resolve) {
        var results = [];
        var expecting = theArray.length;
        theArray.forEach(function(entry, index) {
            doSomethingAsync(entry, function(result) {
                results[index] = result;
                if (--expecting === 0) {
                    // Done!
                    resolve(results);
                }
            });
        });
    });
}
doSomethingWith([1, 2, 3]).then(function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
  max-height: 100% !important;
}

(Hoặc cách khác, bạn có thể tạo một trình bao bọc cho doSomethingAsync trả lại lời hứa và sau đó thực hiện các thao tác dưới đây ...)

Nếu doSomethingAsync cung cấp cho bạn Promise , bạn có thể sử dụng Promise.all :

function doSomethingWith(theArray) {
    return Promise.all(theArray.map(function(entry) {
        return doSomethingAsync(entry);
    }));
}
doSomethingWith(theArray).then(function(results) {
    console.log("Results:", results);
});

Nếu bạn biết rằng doSomethingAsync sẽ bỏ qua đối số thứ hai và thứ ba, bạn có thể chuyển trực tiếp nó tới map (map gọi lại cuộc gọi của nó với ba đối số, nhưng hầu hết mọi người chỉ sử dụng hầu hết thời gian đầu tiên):

function doSomethingWith(theArray) {
    return Promise.all(theArray.map(doSomethingAsync));
}
doSomethingWith(theArray).then(function(results) {
    console.log("Results:", results);
});

Thí dụ:

function doSomethingWith(theArray) {
    return Promise.all(theArray.map(doSomethingAsync));
}
doSomethingWith([1, 2, 3]).then(function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value) {
    console.log("Starting async operation for " + value);
    return new Promise(function(resolve) {
        setTimeout(function() {
            console.log("Completing async operation for " + value);
            resolve(value * 2);
        }, Math.floor(Math.random() * 200));
    });
}
.as-console-wrapper {
  max-height: 100% !important;
}

Lưu ý rằng Promise.all giải quyết lời hứa của mình bằng một loạt kết quả của tất cả các lời hứa mà bạn đưa ra khi tất cả được giải quyết hoặc từ chối lời hứa của nó khi đầu tiên trong số những lời hứa bạn đưa ra từ chối.

Loạt

Giả sử bạn không muốn các hoạt động song song? Nếu bạn muốn chạy từng cái một, bạn cần đợi cho mỗi thao tác hoàn thành trước khi bạn bắt đầu tiếp theo. Đây là một ví dụ về chức năng thực hiện điều đó và gọi một cuộc gọi lại với kết quả:

function doSomethingWith(theArray, callback) {
    var results = [];
    doOne(0);
    function doOne(index) {
        if (index < theArray.length) {
            doSomethingAsync(theArray[index], function(result) {
                results.Push(result);
                doOne(index + 1);
            });
        } else {
            // Done!
            callback(results);
        }
    }
}
doSomethingWith(theArray, function(results) {
    console.log("Results:", results);
});

(Vì chúng tôi đang thực hiện công việc theo chuỗi, chúng tôi chỉ có thể sử dụng results.Push(result) vì chúng tôi biết rằng chúng tôi sẽ không nhận được kết quả theo thứ tự. Ở trên, chúng tôi có thể đã sử dụng results[index] = result;, nhưng trong một số ví dụ sau chúng tôi không 't có một chỉ mục để sử dụng.)

Thí dụ:

function doSomethingWith(theArray, callback) {
    var results = [];
    doOne(0);
    function doOne(index) {
        if (index < theArray.length) {
            doSomethingAsync(theArray[index], function(result) {
                results.Push(result);
                doOne(index + 1);
            });
        } else {
            // Done!
            callback(results);
        }
    }
}
doSomethingWith([1, 2, 3], function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
  max-height: 100% !important;
}

(Hoặc, một lần nữa, xây dựng trình bao bọc cho doSomethingAsync cung cấp cho bạn một lời hứa và thực hiện các thao tác dưới đây ...)

Nếu doSomethingAsync cung cấp cho bạn một Promise, nếu bạn có thể sử dụng cú pháp ES2017 + (có lẽ với bộ chuyển mã như Babel ), bạn có thể sử dụng hàm async với for-ofawait :

async function doSomethingWith(theArray) {
    const results = [];
    for (const entry of theArray) {
        results.Push(await doSomethingAsync(entry));
    }
    return results;
}
doSomethingWith(theArray).then(results => {
    console.log("Results:", results);
});

Thí dụ:

async function doSomethingWith(theArray) {
    const results = [];
    for (const entry of theArray) {
        results.Push(await doSomethingAsync(entry));
    }
    return results;
}
doSomethingWith([1, 2, 3]).then(function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value) {
    console.log("Starting async operation for " + value);
    return new Promise(function(resolve) {
        setTimeout(function() {
            console.log("Completing async operation for " + value);
            resolve(value * 2);
        }, Math.floor(Math.random() * 200));
    });
}
.as-console-wrapper {
  max-height: 100% !important;
}

Nếu bạn không thể sử dụng cú pháp ES2017 + (chưa), bạn có thể sử dụng một biến thể trên mẫu "Promise giảm" (điều này phức tạp hơn so với giảm Promise thông thường vì chúng tôi không chuyển kết quả từ một vào tiếp theo, nhưng thay vào đó thu thập kết quả của họ trong một mảng):

function doSomethingWith(theArray) {
    return theArray.reduce(function(p, entry) {
        return p.then(function(results) {
            return doSomethingAsync(entry).then(function(result) {
                results.Push(result);
                return results;
            });
        });
    }, Promise.resolve([]));
}
doSomethingWith(theArray).then(function(results) {
    console.log("Results:", results);
});

Thí dụ:

function doSomethingWith(theArray) {
    return theArray.reduce(function(p, entry) {
        return p.then(function(results) {
            return doSomethingAsync(entry).then(function(result) {
                results.Push(result);
                return results;
            });
        });
    }, Promise.resolve([]));
}
doSomethingWith([1, 2, 3]).then(function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value) {
    console.log("Starting async operation for " + value);
    return new Promise(function(resolve) {
        setTimeout(function() {
            console.log("Completing async operation for " + value);
            resolve(value * 2);
        }, Math.floor(Math.random() * 200));
    });
}
.as-console-wrapper {
  max-height: 100% !important;
}

... ít cồng kềnh hơn với các hàm mũi tên ES2015 + :

function doSomethingWith(theArray) {
    return theArray.reduce((p, entry) => p.then(results => doSomethingAsync(entry).then(result => {
        results.Push(result);
        return results;
    })), Promise.resolve([]));
}
doSomethingWith(theArray).then(results => {
    console.log("Results:", results);
});

Thí dụ:

function doSomethingWith(theArray) {
    return theArray.reduce((p, entry) => p.then(results => doSomethingAsync(entry).then(result => {
        results.Push(result);
        return results;
    })), Promise.resolve([]));
}
doSomethingWith([1, 2, 3]).then(function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value) {
    console.log("Starting async operation for " + value);
    return new Promise(function(resolve) {
        setTimeout(function() {
            console.log("Completing async operation for " + value);
            resolve(value * 2);
        }, Math.floor(Math.random() * 200));
    });
}
.as-console-wrapper {
  max-height: 100% !important;
}
122
T.J. Crowder

Hãy xem ví dụ này:

var app = angular.module('plunker', []);

app.controller('MainCtrl', function($scope,$http) {

    var getJoke = function(){
        return $http.get('http://api.icndb.com/jokes/random').then(function(res){
            return res.data.value;  
        });
    }

    getJoke().then(function(res) {
        console.log(res.joke);
    });
});

Như bạn có thể thấy getJoke trả về a đã giải quyết lời hứa (nó được giải quyết khi trả về res.data.value). Vì vậy, bạn đợi cho đến khi $ http.get request hoàn thành và sau đó console.log (res.joke) được thực thi (như một luồng không đồng bộ thông thường).

Đây là plnkr:

http://embed.plnkr.co/XlNR7HpCaIhJxskMJfSg/

Cách ES6 (không đồng bộ - đang chờ)

(function(){
  async function getJoke(){
    let response = await fetch('http://api.icndb.com/jokes/random');
    let data = await response.json();
    return data.value;
  }

  getJoke().then((joke) => {
    console.log(joke);
  });
})();
97
Francisco Carmona

Một cách tiếp cận khác để trả về một giá trị từ hàm không đồng bộ, là truyền vào một đối tượng sẽ lưu trữ kết quả từ hàm không đồng bộ.

Đây là một ví dụ tương tự:

var async = require("async");

// This wires up result back to the caller
var result = {};
var asyncTasks = [];
asyncTasks.Push(function(_callback){
    // some asynchronous operation
    $.ajax({
        url: '...',
        success: function(response) {
            result.response = response;
            _callback();
        }
    });
});

async.parallel(asyncTasks, function(){
    // result is available after performing asynchronous operation
    console.log(result)
    console.log('Done');
});

Tôi đang sử dụng đối tượng result để lưu trữ giá trị trong quá trình hoạt động không đồng bộ. Điều này cho phép kết quả có sẵn ngay cả sau khi công việc không đồng bộ.

Tôi sử dụng phương pháp này rất nhiều. Tôi sẽ quan tâm để biết cách tiếp cận này hoạt động tốt như thế nào khi kết nối lại kết quả thông qua các mô-đun liên tiếp.

85
jsbisht

Đây là một trong những nơi hai cách liên kết dữ liệu được sử dụng trong nhiều khung JavaScript mới sẽ hoạt động rất tốt cho bạn ...

Vì vậy, nếu bạn đang sử dụng Angular, React hoặc bất kỳ khung công tác nào khác làm hai cách liên kết dữ liệu, vấn đề này chỉ đơn giản là khắc phục cho bạn, vì vậy, trong Word dễ dàng, kết quả của bạn là undefined giai đoạn đầu tiên, vì vậy bạn đã có result = undefined trước khi bạn nhận được dữ liệu, ngay sau khi bạn nhận được kết quả, nó sẽ được cập nhật và được gán cho giá trị mới đáp ứng cuộc gọi Ajax của bạn ...

Nhưng làm thế nào bạn có thể làm điều đó trong thuần javascript hoặc jQuery ví dụ như bạn đã hỏi trong câu hỏi này?

Bạn có thể sử dụng a gọi lại , lời hứa và gần đây có thể quan sát để xử lý nó cho bạn, ví dụ như trong các lời hứa, chúng tôi có một số chức năng như thành công () hoặc sau đó () sẽ được thực thi khi dữ liệu của bạn đã sẵn sàng cho bạn, tương tự với gọi lại hoặc đăng ký function on obsable .

Ví dụ: trong trường hợp bạn đang sử dụng jQuery , bạn có thể làm một cái gì đó như thế này:

$(document).ready(function(){
    function foo() {
        $.ajax({url: "api/data", success: function(data){
            fooDone(data); //after we have data, we pass it to fooDone
        }});
    };

    function fooDone(data) {
        console.log(data); //fooDone has the data and console.log it
    };

    foo(); //call happens here
});

Để biết thêm thông tin, hãy nghiên cứu về lời hứa có thể quan sát đó là những cách mới hơn để thực hiện công cụ không đồng bộ này.

80
Alireza

Mặc dù lời hứa và cuộc gọi lại hoạt động tốt trong nhiều tình huống, nhưng thật khó để thể hiện điều gì đó như:

if (!name) {
  name = async1();
}
async2(name);

Cuối cùng, bạn sẽ trải qua async1; kiểm tra xem name có được xác định hay không và gọi lại cho phù hợp.

async1(name, callback) {
  if (name)
    callback(name)
  else {
    doSomething(callback)
  }
}

async1(name, async2)

Mặc dù nó là okay trong các ví dụ nhỏ, nó gây khó chịu khi bạn có nhiều trường hợp tương tự và xử lý lỗi liên quan.

Fibers giúp giải quyết vấn đề.

var Fiber = require('fibers')

function async1(container) {
  var current = Fiber.current
  var result
  doSomething(function(name) {
    result = name
    fiber.run()
  })
  Fiber.yield()
  return result
}

Fiber(function() {
  var name
  if (!name) {
    name = async1()
  }
  async2(name)
  // Make any number of async calls from here
}

Bạn có thể kiểm tra dự án tại đây .

77
rohithpr

Câu trả lời ngắn gọn là, bạn phải thực hiện một cuộc gọi lại như thế này:

function callback(response) {
    // Here you can do what ever you want with the response object.
    console.log(response);
}

$.ajax({
    url: "...",
    success: callback
});
73
Pablo Matias Gomez

Ví dụ sau đây tôi đã viết cho thấy làm thế nào để

  • Xử lý các cuộc gọi HTTP không đồng bộ;
  • Đợi phản hồi từ mỗi lệnh gọi API;
  • Sử dụng Promise mẫu;
  • Sử dụng Promise.all mẫu để tham gia nhiều cuộc gọi HTTP;

Ví dụ làm việc này là khép kín. Nó sẽ định nghĩa một đối tượng yêu cầu đơn giản sử dụng đối tượng XMLHttpRequest của cửa sổ để thực hiện cuộc gọi. Nó sẽ xác định một chức năng đơn giản để chờ đợi một loạt các lời hứa sẽ được hoàn thành.

Bối cảnh. Ví dụ đang truy vấn Spotify Web API endpoint để tìm kiếm các đối tượng playlist cho một chuỗi các chuỗi truy vấn nhất định:

[
 "search?type=playlist&q=%22Doom%20metal%22",
 "search?type=playlist&q=Adele"
]

Đối với mỗi mục, Promise mới sẽ kích hoạt một khối - ExecutionBlock, phân tích kết quả, lên lịch cho một loạt các lời hứa mới dựa trên mảng kết quả, đó là danh sách các đối tượng Spotify user và thực hiện cuộc gọi HTTP mới trong ExecutionProfileBlock không đồng bộ.

Sau đó, bạn có thể thấy cấu trúc Promise lồng nhau, cho phép bạn sinh ra nhiều cuộc gọi HTTP lồng nhau và hoàn toàn không đồng bộ và tham gia kết quả từ mỗi tập hợp con của các cuộc gọi thông qua Promise.all.

NOTEAPI Spotify search gần đây sẽ yêu cầu mã thông báo truy cập được chỉ định trong tiêu đề yêu cầu:

-H "Authorization: Bearer {your access token}" 

Vì vậy, bạn để chạy ví dụ sau bạn cần đặt mã thông báo truy cập của mình vào tiêu đề yêu cầu:

var spotifyAccessToken = "YourSpotifyAccessToken";
var console = {
    log: function(s) {
        document.getElementById("console").innerHTML += s + "<br/>"
    }
}

// Simple XMLHttpRequest
// based on https://davidwalsh.name/xmlhttprequest
SimpleRequest = {
    call: function(what, response) {
        var request;
        if (window.XMLHttpRequest) { // Mozilla, Safari, ...
            request = new XMLHttpRequest();
        } else if (window.ActiveXObject) { // Internet Explorer
            try {
                request = new ActiveXObject('Msxml2.XMLHTTP');
            }
            catch (e) {
                try {
                  request = new ActiveXObject('Microsoft.XMLHTTP');
                } catch (e) {}
            }
        }

        // State changes
        request.onreadystatechange = function() {
            if (request.readyState === 4) { // Done
                if (request.status === 200) { // Complete
                    response(request.responseText)
                }
                else
                    response();
            }
        }
        request.open('GET', what, true);
        request.setRequestHeader("Authorization", "Bearer " + spotifyAccessToken);
        request.send(null);
    }
}

//PromiseAll
var promiseAll = function(items, block, done, fail) {
    var self = this;
    var promises = [],
                   index = 0;
    items.forEach(function(item) {
        promises.Push(function(item, i) {
            return new Promise(function(resolve, reject) {
                if (block) {
                    block.apply(this, [item, index, resolve, reject]);
                }
            });
        }(item, ++index))
    });
    Promise.all(promises).then(function AcceptHandler(results) {
        if (done) done(results);
    }, function ErrorHandler(error) {
        if (fail) fail(error);
    });
}; //promiseAll

// LP: deferred execution block
var ExecutionBlock = function(item, index, resolve, reject) {
    var url = "https://api.spotify.com/v1/"
    url += item;
    console.log( url )
    SimpleRequest.call(url, function(result) {
        if (result) {

            var profileUrls = JSON.parse(result).playlists.items.map(function(item, index) {
                return item.owner.href;
            })
            resolve(profileUrls);
        }
        else {
            reject(new Error("call error"));
        }
    })
}

arr = [
    "search?type=playlist&q=%22Doom%20metal%22",
    "search?type=playlist&q=Adele"
]

promiseAll(arr, function(item, index, resolve, reject) {
    console.log("Making request [" + index + "]")
    ExecutionBlock(item, index, resolve, reject);
}, function(results) { // Aggregated results

    console.log("All profiles received " + results.length);
    //console.log(JSON.stringify(results[0], null, 2));

    ///// promiseall again

    var ExecutionProfileBlock = function(item, index, resolve, reject) {
        SimpleRequest.call(item, function(result) {
            if (result) {
                var obj = JSON.parse(result);
                resolve({
                    name: obj.display_name,
                    followers: obj.followers.total,
                    url: obj.href
                });
            } //result
        })
    } //ExecutionProfileBlock

    promiseAll(results[0], function(item, index, resolve, reject) {
        //console.log("Making request [" + index + "] " + item)
        ExecutionProfileBlock(item, index, resolve, reject);
    }, function(results) { // aggregated results
        console.log("All response received " + results.length);
        console.log(JSON.stringify(results, null, 2));
    }

    , function(error) { // Error
        console.log(error);
    })

    /////

  },
  function(error) { // Error
      console.log(error);
  });
<div id="console" />

Tôi đã thảo luận rộng rãi về giải pháp này ở đây .

70
loretoparisi

Câu trả lời năm 2017: bây giờ bạn có thể làm chính xác những gì bạn muốn trong mọi trình duyệt và nút hiện tại

Điều này khá đơn giản:

  • Trả lại một lời hứa
  • Sử dụng 'await' , sẽ báo cho JavaScript để chờ lời hứa được giải quyết thành một giá trị (như phản hồi HTTP)
  • Thêm 'async' từ khóa vào hàm cha

Đây là phiên bản làm việc của mã của bạn:

(async function(){

var response = await superagent.get('...')
console.log(response)

})()

await được hỗ trợ trong tất cả các trình duyệt hiện tại và nút 8

67
mikemaccana

Bạn có thể sử dụng thư viện tùy chỉnh này (được viết bằng Promise) để thực hiện cuộc gọi từ xa.

function $http(apiConfig) {
    return new Promise(function (resolve, reject) {
        var client = new XMLHttpRequest();
        client.open(apiConfig.method, apiConfig.url);
        client.send();
        client.onload = function () {
            if (this.status >= 200 && this.status < 300) {
                // Performs the function "resolve" when this.status is equal to 2xx.
                // Your logic here.
                resolve(this.response);
            }
            else {
                // Performs the function "reject" when this.status is different than 2xx.
                reject(this.statusText);
            }
        };
        client.onerror = function () {
            reject(this.statusText);
        };
    });
}

Ví dụ sử dụng đơn giản:

$http({
    method: 'get',
    url: 'google.com'
}).then(function(response) {
    console.log(response);
}, function(error) {
    console.log(error)
});
60
Vinoth Rajendran

Js là một luồng đơn.

Trình duyệt có thể được chia thành ba phần:

1) Vòng lặp sự kiện

2) API web

3) Hàng đợi sự kiện

Vòng lặp sự kiện chạy mãi mãi, tức là loại vòng lặp vô hạn. Hàng đợi là nơi tất cả chức năng của bạn được đẩy vào một số sự kiện (ví dụ: nhấp chuột) lần lượt được thực hiện trong hàng đợi và đưa vào vòng lặp Sự kiện thực hiện chức năng này và tự chuẩn bị nó cho lần tiếp theo sau lần đầu tiên được thực thi. Điều này có nghĩa là Thực thi một chức năng không bắt đầu cho đến khi chức năng trước khi hàng đợi được thực thi trong vòng lặp sự kiện.

Bây giờ chúng ta hãy nghĩ rằng chúng ta đã đẩy hai hàm trong một hàng đợi là để lấy dữ liệu từ máy chủ và một hàm khác sử dụng dữ liệu đó. Chúng ta đã đẩy hàm serverRequest () trong hàng đợi trước sau đó là hàm useiseData (). Hàm serverRequest đi vào vòng lặp sự kiện và thực hiện cuộc gọi đến máy chủ vì chúng tôi không bao giờ biết sẽ mất bao nhiêu thời gian để lấy dữ liệu từ máy chủ, vì vậy quá trình này được dự kiến ​​sẽ mất thời gian và vì vậy chúng tôi bận rộn vòng lặp sự kiện của chúng tôi, do đó là trang Web API đi vào vai trò, nó lấy chức năng này từ vòng lặp sự kiện và giao dịch với máy chủ làm cho vòng lặp sự kiện miễn phí để chúng ta có thể thực thi chức năng tiếp theo từ hàng đợi. Hàm tiếp theo trong hàng đợi là produciseData () đi theo vòng lặp nhưng vì không có dữ liệu nên nó đi lãng phí và thực thi chức năng tiếp theo tiếp tục cho đến khi kết thúc hàng đợi (Đây được gọi là cuộc gọi Async tức là chúng ta có thể làm gì đó khác cho đến khi nhận được dữ liệu)

Giả sử hàm serverRequest () của chúng ta có câu lệnh return trong mã, khi chúng ta lấy lại dữ liệu từ API Web của máy chủ sẽ đẩy nó vào hàng đợi ở cuối hàng đợi. Khi nó bị đẩy vào cuối hàng đợi, chúng tôi không thể sử dụng dữ liệu của nó vì không còn chức năng nào trong hàng đợi của chúng tôi để sử dụng dữ liệu này . Do đó, không thể trả lại một cái gì đó từ Cuộc gọi Async.

Do đó, Giải pháp cho vấn đề này là gọi lại hoặc lời hứa.

Một hình ảnh từ một trong những câu trả lời ở đây, Giải thích chính xác việc sử dụng gọi lại ... Chúng tôi cung cấp chức năng của mình (chức năng sử dụng dữ liệu được trả về từ máy chủ) cho chức năng gọi máy chủ.

 CallBack

 function doAjax(callbackFunc, method, url) {
  var xmlHttpReq = new XMLHttpRequest();
  xmlHttpReq.open(method, url);
  xmlHttpReq.onreadystatechange = function() {

      if (xmlHttpReq.readyState == 4 && xmlHttpReq.status == 200) {
        callbackFunc(xmlHttpReq.responseText);
      }


  }
  xmlHttpReq.send(null);

}

Trong mã của tôi, nó được gọi là

function loadMyJson(categoryValue){
  if(categoryValue==="veg")
  doAjax(print,"GET","http://localhost:3004/vegetables");
  else if(categoryValue==="fruits")
  doAjax(print,"GET","http://localhost:3004/fruits");
  else 
  console.log("Data not found");
}

Đọc ở đây để biết các phương thức mới trong ECMA (2016/17) để thực hiện cuộc gọi không đồng bộ (@Felix Kling Trả lời trên đầu) https://stackoverflow.com/a/14220323/7579856

55
Aniket Jha

Một giải pháp khác là thực thi mã thông qua trình thực thi tuần tự nsynjs .

Nếu chức năng cơ bản được hứa hẹn

nsynjs sẽ đánh giá tất cả các lời hứa một cách tuần tự và đưa kết quả lời hứa vào thuộc tính data:

function synchronousCode() {

    var getURL = function(url) {
        return window.fetch(url).data.text().data;
    };
    
    var url = 'https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js';
    console.log('received bytes:',getURL(url).length);
    
};

nsynjs.run(synchronousCode,{},function(){
    console.log('synchronousCode done');
});
<script src="https://rawgit.com/amaksr/nsynjs/master/nsynjs.js"></script>

Nếu chức năng cơ bản không được hứa hẹn

Bước 1. Gói chức năng với hàm gọi lại vào trình bao bọc nhận biết nsynjs (nếu nó có phiên bản được quảng cáo, bạn có thể bỏ qua bước này):

var ajaxGet = function (ctx,url) {
    var res = {};
    var ex;
    $.ajax(url)
    .done(function (data) {
        res.data = data;
    })
    .fail(function(e) {
        ex = e;
    })
    .always(function() {
        ctx.resume(ex);
    });
    return res;
};
ajaxGet.nsynjsHasCallback = true;

Bước 2. Đặt logic đồng bộ vào chức năng:

function process() {
    console.log('got data:', ajaxGet(nsynjsCtx, "data/file1.json").data);
}

Bước 3. Chạy chức năng đồng bộ qua nsynjs:

nsynjs.run(process,this,function () {
    console.log("synchronous function finished");
});

Nsynjs sẽ đánh giá tất cả các toán tử và các biểu thức từng bước, tạm dừng thực thi trong trường hợp nếu kết quả của một số chức năng chậm chưa sẵn sàng.

Thêm ví dụ tại đây: https://github.com/amaksr/nsynjs/tree/master/examples

54
amaksr

Đây là một vấn đề rất phổ biến mà chúng ta gặp phải trong khi đấu tranh với 'bí ẩn' của JavaScript. Hãy để tôi thử làm sáng tỏ bí ẩn này ngày hôm nay.

Hãy bắt đầu với một hàm JavaScript đơn giản:

function foo(){
// do something 
 return 'wohoo';
}

let bar = foo(); // bar is 'wohoo' here

Đó là một lệnh gọi hàm đồng bộ đơn giản (trong đó mỗi dòng mã được 'hoàn thành với công việc của nó' trước chuỗi tiếp theo theo thứ tự) và kết quả giống như mong đợi.

Bây giờ, hãy thêm một chút thay đổi, bằng cách giới thiệu một chút độ trễ trong hàm của chúng ta, để tất cả các dòng mã không được 'hoàn thành' theo trình tự. Do đó, nó sẽ mô phỏng hành vi không đồng bộ của hàm:

function foo(){
 setTimeout( ()=>{
   return 'wohoo';
  }, 1000 )
}

let bar = foo() // bar is undefined here

Vì vậy, bạn đi, sự chậm trễ đó chỉ phá vỡ chức năng chúng tôi mong đợi! Nhưng chính xác thì chuyện gì đã xảy ra? Chà, nó thực sự khá logic nếu bạn nhìn vào mã. hàm foo(), khi thực thi, không trả về giá trị nào (do đó giá trị được trả về là undefined), nhưng nó khởi động bộ đếm thời gian, thực thi một hàm sau 1 giây để trả về 'wohoo'. Nhưng như bạn có thể thấy, giá trị được gán cho thanh là thứ được trả về ngay lập tức từ foo (), chứ không phải bất cứ thứ gì khác đến sau.

Vậy, làm thế nào để chúng ta giải quyết vấn đề này?

Hãy hỏi chức năng của chúng tôi cho mộtLỜI HỨA. Promise thực sự là về ý nghĩa của nó: nó có nghĩa là hàm đảm bảo cho bạn cung cấp bất kỳ đầu ra nào trong tương lai. vì vậy hãy xem nó hoạt động cho vấn đề nhỏ của chúng tôi ở trên:

function foo(){
   return new Promise( (resolve, reject) => { // I want foo() to PROMISE me something
    setTimeout ( function(){ 
      // promise is RESOLVED , when execution reaches this line of code
       resolve('wohoo')// After 1 second, RESOLVE the promise with value 'wohoo'
    }, 1000 )
  })
}

let bar ; 
foo().then( res => {
 bar = res;
 console.log(bar) // will print 'wohoo'
});

Do đó, tóm tắt là - để giải quyết các chức năng không đồng bộ như các cuộc gọi dựa trên ajax, v.v., bạn có thể sử dụng lời hứa để resolve giá trị (mà bạn dự định trả về). Do đó, trong ngắn hạn, bạn giải quyết giá trị thay vì trả về , trong các hàm không đồng bộ.

CẬP NHẬT (Hứa với async/await)

Ngoài việc sử dụng then/catch để thực hiện với các lời hứa, còn có một cách tiếp cận khác. Ý tưởng là nhận ra một hàm không đồng bộ và sau đó chờ đợi những lời hứa để giải quyết, trước khi chuyển sang dòng mã tiếp theo. Nó vẫn chỉ là promises dưới mui xe, nhưng với một cách tiếp cận cú pháp khác. Để làm cho mọi thứ rõ ràng hơn, bạn có thể tìm thấy một so sánh dưới đây:

sau đó/bắt phiên bản:

function fetchUsers(){
   let users = [];
   getUsers()
   .then(_users => users = _users)
   .catch(err =>{
      throw err
   })
   return users;
 }

async/await phiên bản:

  async function fetchUsers(){
     try{
        let users = await getUsers()
        return users;
     }
     catch(err){
        throw err;
     }
  }
42
Anish K.

ECMAScript 6 có 'bộ tạo' cho phép bạn dễ dàng lập trình theo kiểu không đồng bộ.

function* myGenerator() {
    const callback = yield;
    let [response] = yield $.ajax("https://stackoverflow.com", {complete: callback});
    console.log("response is:", response);

    // examples of other things you can do
    yield setTimeout(callback, 1000);
    console.log("it delayed for 1000ms");
    while (response.statusText === "error") {
        [response] = yield* anotherGenerator();
    }
}

Để chạy mã trên, bạn làm điều này:

const gen = myGenerator(); // Create generator
gen.next(); // Start it
gen.next((...args) => gen.next([...args])); // Set its callback function

Nếu bạn cần nhắm mục tiêu các trình duyệt không hỗ trợ ES6, bạn có thể chạy mã thông qua Babel hoặc trình biên dịch đóng để tạo ECMAScript 5.

Cuộc gọi lại ...args được gói trong một mảng và bị hủy khi bạn đọc chúng để mẫu có thể đối phó với các cuộc gọi lại có nhiều đối số. Ví dụ với nút fs :

const [err, data] = yield fs.readFile(filePath, "utf-8", callback);
36
James

Dưới đây là một số cách tiếp cận để làm việc với các yêu cầu không đồng bộ:

  1. Đối tượng Promise trình duyệt
  2. Q - Một thư viện hứa cho JavaScript
  3. A + Promising.js
  4. jQuery hoãn lại
  5. API XMLHttpRequest
  6. Sử dụng khái niệm gọi lại - Khi thực hiện trong câu trả lời đầu tiên

Ví dụ: jQuery trì hoãn triển khai để làm việc với nhiều yêu cầu

var App = App || {};

App = {
    getDataFromServer: function(){

      var self = this,
                 deferred = $.Deferred(),
                 requests = [];

      requests.Push($.getJSON('request/ajax/url/1'));
      requests.Push($.getJSON('request/ajax/url/2'));

      $.when.apply(jQuery, requests).done(function(xhrResponse) {
        return deferred.resolve(xhrResponse.result);
      });
      return deferred;
    },

    init: function(){

        this.getDataFromServer().done(_.bind(function(resp1, resp2) {

           // Do the operations which you wanted to do when you
           // get a response from Ajax, for example, log response.
        }, this));
    }
};
App.init();
36
Mohan Dere

Câu trả lời ngắn : Phương thức foo() của bạn trả về ngay lập tức, trong khi cuộc gọi $ajax() thực thi không đồng bộ sau khi hàm trả về . Vấn đề là sau đó làm thế nào hoặc ở đâu để lưu trữ các kết quả được truy xuất bằng lệnh gọi async sau khi nó trả về.

Một số giải pháp đã được đưa ra trong chủ đề này. Có lẽ cách dễ nhất là chuyển một đối tượng sang phương thức foo() và lưu trữ kết quả trong một thành viên của đối tượng đó sau khi cuộc gọi async hoàn thành.

function foo(result) {
    $.ajax({
        url: '...',
        success: function(response) {
            result.response = response;   // Store the async result
        }
    });
}

var result = { response: null };   // Object to hold the async result
foo(result);                       // Returns before the async completes

Lưu ý rằng cuộc gọi đến foo() vẫn sẽ không có gì hữu ích. Tuy nhiên, kết quả của cuộc gọi không đồng bộ sẽ được lưu trữ trong result.response.

33
David R Tribble

Sử dụng chức năng callback() bên trong thành công foo(). Hãy thử theo cách này. Nó là đơn giản và dễ hiểu.

var lat = "";
var lon = "";
function callback(data) {
    lat = data.lat;
    lon = data.lon;
}
function getLoc() {
    var url = "http://ip-api.com/json"
    $.getJSON(url, function(data) {
        callback(data);
    });
}

getLoc();
33
Mahfuzur Rahman

Chúng ta thấy mình trong một vũ trụ dường như tiến lên theo chiều mà chúng ta gọi là "thời gian". Chúng tôi không thực sự hiểu thời gian là gì, nhưng chúng tôi đã phát triển trừu tượng và từ vựng cho phép chúng tôi suy luận và nói về nó: "quá khứ", "hiện tại", "tương lai", "trước", "sau".

Các hệ thống máy tính chúng tôi xây dựng - ngày càng nhiều hơn - có thời gian là một khía cạnh quan trọng. Một số điều được thiết lập để xảy ra trong tương lai. Sau đó, những điều khác cần phải xảy ra sau khi những điều đầu tiên cuối cùng xảy ra. Đây là khái niệm cơ bản được gọi là "không điển hình". Trong thế giới ngày càng kết nối của chúng ta, trường hợp không điển hình phổ biến nhất đang chờ một hệ thống từ xa đáp ứng một số yêu cầu.

Hãy xem xét một ví dụ. Bạn gọi cho milkman và gọi một ít sữa. Khi nó đến, bạn muốn đặt nó vào cà phê của bạn. Bạn không thể đặt sữa vào cà phê của bạn ngay bây giờ, vì nó chưa ở đây. Bạn phải đợi nó đến trước khi đưa nó vào cà phê của bạn. Nói cách khác, những điều sau đây sẽ không hoạt động:

var milk = order_milk();
put_in_coffee(milk);

Bởi vì JS không có cách nào để biết rằng nó cần chờ đợi cho order_milk để kết thúc trước khi nó thực thi put_in_coffee. Nói cách khác, không biết rằng order_milk không đồng bộ - là thứ sẽ không dẫn đến sữa cho đến một thời điểm nào đó trong tương lai. JS và các ngôn ngữ khai báo khác thực thi một câu lệnh khác mà không cần chờ đợi.

Cách tiếp cận JS cổ điển cho vấn đề này, lợi dụng thực tế là JS hỗ trợ các hàm như các đối tượng hạng nhất có thể được truyền qua, là chuyển một hàm làm tham số cho yêu cầu không đồng bộ, sau đó nó sẽ gọi khi hoàn thành nhiệm vụ của nó đôi khi trong tương lai. Đó là cách tiếp cận "gọi lại". Nó trông như thế này:

order_milk(put_in_coffee);

order_milk khởi động, ra lệnh cho sữa, sau đó, khi và chỉ khi nó đến, nó gọi put_in_coffee.

Vấn đề với cách tiếp cận gọi lại này là nó gây ô nhiễm ngữ nghĩa thông thường của một hàm báo cáo kết quả của nó với return; thay vào đó, các hàm không được báo cáo kết quả của chúng bằng cách gọi một cuộc gọi lại được đưa ra dưới dạng tham số. Ngoài ra, cách tiếp cận này có thể nhanh chóng trở nên khó sử dụng khi xử lý các chuỗi sự kiện dài hơn. Ví dụ: giả sử tôi muốn đợi sữa được đưa vào cà phê, và sau đó và chỉ sau đó thực hiện bước thứ ba, cụ thể là uống cà phê. Tôi cuối cùng cần phải viết một cái gì đó như thế này:

order_milk(function(milk) { put_in_coffee(milk, drink_coffee); }

nơi tôi chuyển đến put_in_coffee cả sữa để đặt vào đó và cả hành động (drink_coffee) để thực thi khi sữa đã được đưa vào. Mã đó trở nên khó viết, đọc và gỡ lỗi.

Trong trường hợp này, chúng tôi có thể viết lại mã trong câu hỏi dưới dạng:

var answer;
$.ajax('/foo.json') . done(function(response) {
  callback(response.data);
});

function callback(data) {
  console.log(data);
}

Nhập lời hứa

Đây là động lực cho khái niệm "lời hứa", một loại giá trị cụ thể đại diện cho tương lai hoặc không đồng bộ kết quả của một loại nào đó. Nó có thể đại diện cho điều gì đó đã xảy ra, hoặc điều đó sẽ xảy ra trong tương lai, hoặc có thể không bao giờ xảy ra. Lời hứa có một phương thức duy nhất, được đặt tên là then, mà bạn vượt qua một hành động sẽ được thực thi khi kết quả mà lời hứa đại diện đã được thực hiện.

Trong trường hợp sữa và cà phê của chúng tôi, chúng tôi thiết kế order_milk để trả lại lời hứa cho sữa đến, sau đó chỉ định put_in_coffee là hành động then, như sau:

order_milk() . then(put_in_coffee)

Một lợi thế của điều này là chúng ta có thể xâu chuỗi những thứ này lại với nhau để tạo ra chuỗi các lần xuất hiện trong tương lai ("chuỗi"):

order_milk() . then(put_in_coffee) . then(drink_coffee)

Hãy áp dụng lời hứa cho vấn đề cụ thể của bạn. Chúng tôi sẽ gói logic yêu cầu của chúng tôi bên trong một hàm, trả về một lời hứa:

function get_data() {
  return $.ajax('/foo.json');
}

Trên thực tế, tất cả những gì chúng tôi đã thực hiện được thêm một return vào cuộc gọi đến $.ajax. Điều này hoạt động vì $.ajax của jQuery đã trả về một loại điều giống như lời hứa. (Trong thực tế, không đi sâu vào chi tiết, chúng tôi muốn thực hiện cuộc gọi này để trả lại lời hứa thực sự hoặc sử dụng một số thay thế cho $.ajax như vậy.) Bây giờ, nếu chúng tôi muốn tải tệp và đợi nó kết thúc và sau đó làm một cái gì đó, chúng ta có thể nói một cách đơn giản

get_data() . then(do_something)

ví dụ,

get_data() . 
  then(function(data) { console.log(data); });

Khi sử dụng lời hứa, cuối cùng chúng ta sẽ chuyển rất nhiều hàm vào then, vì vậy việc sử dụng các hàm mũi tên kiểu ES6 nhỏ gọn hơn thường rất hữu ích:

get_data() . 
  then(data => console.log(data));

Từ khóa async

Nhưng vẫn còn một điều gì đó không hài lòng về việc phải viết mã một chiều nếu đồng bộ và một cách hoàn toàn khác nếu không đồng bộ. Để đồng bộ, chúng tôi viết

a();
b();

nhưng nếu a không đồng bộ, với những lời hứa chúng ta phải viết

a() . then(b);

Ở trên, chúng tôi đã nói, "JS không có cách nào để biết rằng nó cần chờ đợi để cuộc gọi đầu tiên kết thúc trước khi nó thực hiện cuộc gọi thứ hai". Sẽ không hay nếu cómột cách nào đó để nói với JS điều đó? Hóa ra là có - từ khóa await, được sử dụng bên trong một loại hàm đặc biệt gọi là hàm "async". Tính năng này là một phần của phiên bản ES sắp ra mắt nhưng đã có sẵn trong các bộ chuyển đổi, chẳng hạn như Babel được cài đặt sẵn. Điều này cho phép chúng ta chỉ cần viết

async function morning_routine() {
  var milk   = await order_milk();
  var coffee = await put_in_coffee(milk);
  await drink(coffee);
}

Trong trường hợp của bạn, bạn sẽ có thể viết một cái gì đó như

async function foo() {
  data = await get_data();
  console.log(data);
}
27
user663031

Tất nhiên có nhiều cách tiếp cận như yêu cầu đồng bộ, hứa hẹn, nhưng từ kinh nghiệm của tôi, tôi nghĩ bạn nên sử dụng phương pháp gọi lại. Đó là hành vi không đồng bộ của Javascript. Vì vậy, đoạn mã của bạn có thể được viết lại một chút khác nhau:

function foo() {
    var result;

    $.ajax({
        url: '...',
        success: function(response) {
            myCallback(response);
        }
    });

    return result;
}

function myCallback(response) {
    // Does something.
}
26
Khoa Bui

Câu hỏi là:

Làm cách nào để trả về phản hồi từ cuộc gọi không đồng bộ?

mà CÓ THỂ được hiểu là:

Cách tạo không đồng bộ code look đồng bộ ?

Giải pháp sẽ là tránh các cuộc gọi lại và sử dụng kết hợp Lời hứa async/await .

Tôi muốn đưa ra một ví dụ cho một yêu cầu Ajax.

(Mặc dù nó có thể được viết bằng Javascript, tôi thích viết nó bằng Python và biên dịch nó thành Javascript bằng cách sử dụng Transcrypt . Nó sẽ đủ rõ ràng.)

Trước tiên hãy cho phép sử dụng JQuery, để có $ khả dụng dưới dạng S:

__pragma__ ('alias', 'S', '$')

Xác định hàm trả về a Promise , trong trường hợp này là lệnh gọi Ajax:

def read(url: str):
    deferred = S.Deferred()
    S.ajax({'type': "POST", 'url': url, 'data': { },
        'success': lambda d: deferred.resolve(d),
        'error': lambda e: deferred.reject(e)
    })
    return deferred.promise()

Sử dụng không đồng bộ code như thể nó là đồng bộ :

async def readALot():
    try:
        result1 = await read("url_1")
        result2 = await read("url_2")
    except Exception:
        console.warn("Reading a lot failed")
24
Pieter Jan Bonestroo

Thay vì ném mã vào bạn, có 2 khái niệm là chìa khóa để hiểu cách JS xử lý các cuộc gọi lại và tính không đồng bộ. (Ngay cả là một từ?)

Mô hình vòng lặp sự kiện và đồng thời

Có ba điều bạn cần lưu ý; Hàng đợi; vòng lặp sự kiện và ngăn xếp

Theo nghĩa rộng, đơn giản, vòng lặp sự kiện giống như người quản lý dự án, nó liên tục lắng nghe bất kỳ chức năng nào muốn chạy và giao tiếp giữa hàng đợi và ngăn xếp.

while (queue.waitForMessage()) {
   queue.processNextMessage();
}

Một khi nó nhận được một tin nhắn để chạy một cái gì đó, nó sẽ thêm nó vào hàng đợi. Hàng đợi là danh sách những thứ đang chờ để thực thi (như yêu cầu AJAX) của bạn. hãy tưởng tượng nó như thế này:

 1. call foo.com/api/bar using foobarFunc
 2. Go perform an infinite loop
 ... and so on

Khi một trong những thông báo này sẽ thực thi, nó sẽ bật thông báo từ hàng đợi và tạo một ngăn xếp, ngăn xếp là tất cả mọi thứ mà JS cần thực hiện để thực hiện hướng dẫn trong thông báo. Vì vậy, trong ví dụ của chúng tôi, nó được yêu cầu gọi foobarFunc

function foobarFunc (var) {
  console.log(anotherFunction(var));
}

Vì vậy, bất cứ điều gì mà foobarFunc cần thực thi (trong trường hợp của chúng tôi anotherFunction) sẽ được đẩy lên ngăn xếp. được thực thi và sau đó quên về - vòng lặp sự kiện sẽ chuyển sang điều tiếp theo trong hàng đợi (hoặc nghe tin nhắn)

Điều quan trọng ở đây là thứ tự thực hiện. Đó là

KHI NÀO là thứ gì đó sẽ chạy

Khi bạn thực hiện cuộc gọi bằng cách sử dụng AJAX cho bên ngoài hoặc chạy bất kỳ mã không đồng bộ nào (ví dụ như setTimeout), Javascript sẽ phụ thuộc vào phản hồi trước khi có thể tiến hành.

Câu hỏi lớn là khi nào nó sẽ nhận được phản hồi? Câu trả lời là chúng tôi không biết - vì vậy, vòng lặp sự kiện đang chờ thông báo đó nói "hey run me". Nếu JS chỉ chờ đợi thông báo đó một cách đồng bộ thì ứng dụng của bạn sẽ đóng băng và nó sẽ bị hút. Vì vậy, JS tiếp tục thực hiện mục tiếp theo trong hàng đợi trong khi chờ thông báo được thêm lại vào hàng đợi.

Đó là lý do tại sao với chức năng không đồng bộ, chúng tôi sử dụng những thứ gọi là cuộc gọi lại . Nó giống như một lời hứa hoàn toàn theo nghĩa đen. Như trong I hứa sẽ trả lại một cái gì đó tại một số điểm jQuery sử dụng các cuộc gọi lại cụ thể được gọi là deffered.donedeffered.faildeffered.always (trong số những người khác). Bạn có thể thấy tất cả tại đây

Vì vậy, những gì bạn cần làm là vượt qua một chức năng được hứa hẹn sẽ thực thi tại một số điểm với dữ liệu được truyền cho nó.

Bởi vì một cuộc gọi lại không được thực thi ngay lập tức, nhưng sau đó, điều quan trọng là chuyển tham chiếu đến hàm không được thực thi. vì thế

function foo(bla) {
  console.log(bla)
}

vì vậy, hầu hết thời gian (nhưng không phải luôn luôn) bạn sẽ vượt qua foo không phải foo()

Hy vọng rằng sẽ có ý nghĩa. Khi bạn gặp phải những điều như thế này có vẻ khó hiểu - tôi khuyên bạn nên đọc tài liệu đầy đủ để ít nhất hiểu được về nó. Nó sẽ làm cho bạn một nhà phát triển tốt hơn nhiều.

16
Matthew Brent

Sử dụng ES2017, bạn nên có cái này như là khai báo hàm

async function foo() {
    var response = await $.ajax({url: '...'})
    return response;
}

Và thực hiện nó như thế này.

(async function() {
    try {
        var result = await foo()
        console.log(result)
    } catch (e) {}
})()

Hoặc cú pháp Promise

foo().then(response => {
    console.log(response)

}).catch(error => {
    console.log(error)

})
14
Fernando Carvajal

Chúng ta hãy nhìn thấy khu rừng trước khi nhìn vào những cái cây.

Có nhiều câu trả lời thông tin với các chi tiết tuyệt vời ở đây, tôi sẽ không lặp lại bất kỳ câu hỏi nào trong số đó. Chìa khóa để lập trình trong JavaScript là đầu tiên có mô hình tinh thần chính xác của việc thực hiện tổng thể.

  1. (Các) điểm vào của bạn được thực hiện như là kết quả của một sự kiện. Ví dụ: thẻ script có mã được tải vào trình duyệt. (Theo đó, đây là lý do tại sao bạn có thể cần quan tâm đến sự sẵn sàng của trang để chạy mã của mình nếu nó yêu cầu các phần tử dom được xây dựng trước, v.v.)
  2. Mã của bạn thực thi để hoàn thành - tuy nhiên nhiều cuộc gọi không đồng bộ mà nó thực hiện - mà không thực hiện bất kỳ trong số các cuộc gọi lại của bạn, bao gồm các yêu cầu XHR, đặt thời gian chờ, xử lý sự kiện dom, v.v. một hàng đợi, chờ đến lượt mình được chạy sau khi các sự kiện khác đã bắn xong tất cả đã thực hiện xong.
  3. Mỗi cuộc gọi lại riêng lẻ cho một yêu cầu XHR, đặt thời gian chờ hoặc dom sự kiện một khi được gọi sẽ chạy đến khi hoàn thành.

Tin tốt là nếu bạn hiểu rõ điểm này, bạn sẽ không bao giờ phải lo lắng về điều kiện chủng tộc. Trước tiên, bạn nên biết cách bạn muốn tổ chức mã của mình về cơ bản là phản ứng với các sự kiện riêng biệt khác nhau và cách bạn muốn xâu chuỗi chúng lại với nhau thành một chuỗi logic. Bạn có thể sử dụng lời hứa hoặc async/await mới ở cấp độ cao hơn làm công cụ cho mục đích đó hoặc bạn có thể tự cuộn.

Nhưng bạn không nên sử dụng bất kỳ công cụ chiến thuật nào để giải quyết vấn đề cho đến khi bạn cảm thấy thoải mái với miền vấn đề thực tế. Vẽ bản đồ của những phụ thuộc này để biết những gì cần chạy khi nào. Cố gắng một cách tiếp cận đặc biệt cho tất cả các cuộc gọi lại này sẽ không phục vụ tốt cho bạn.

13
Haim Zamir

Sử dụng lời hứa

Câu trả lời hoàn hảo nhất cho câu hỏi này là sử dụng Promise.

function ajax(method, url, params) {
  return new Promise(function(resolve, reject) {
    var xhr = new XMLHttpRequest();
    xhr.onload = function() {
      resolve(this.responseText);
    };
    xhr.onerror = reject;
    xhr.open(method, url);
    xhr.send(params);
  });
}

Sử dụng

ajax("GET", "/test", "acrive=1").then(function(result) {
    // Code depending on result
})
.catch(function() {
    // An error occurred
});

Nhưng đợi đã ...!

Có một vấn đề với việc sử dụng lời hứa!

Tại sao chúng ta nên sử dụng Promise tùy chỉnh của riêng mình?

Tôi đã sử dụng giải pháp này một thời gian cho đến khi tôi phát hiện ra có lỗi trong các trình duyệt cũ:

Uncaught ReferenceError: Promise is not defined

Vì vậy, tôi quyết định triển khai lớp Promise của riêng mình cho ES3 bên dưới js trình biên dịch nếu nó không được xác định. Chỉ cần thêm mã này trước mã chính của bạn và sau đó là người dùng an toàn Hứa hẹn!

if(typeof Promise === "undefined"){
    function _classCallCheck(instance, Constructor) {
        if (!(instance instanceof Constructor)) { 
            throw new TypeError("Cannot call a class as a function"); 
        }
    }
    var Promise = function () {
        function Promise(main) {
            var _this = this;
            _classCallCheck(this, Promise);
            this.value = undefined;
            this.callbacks = [];
            var resolve = function resolve(resolveValue) {
                _this.value = resolveValue;
                _this.triggerCallbacks();
            };
            var reject = function reject(rejectValue) {
                _this.value = rejectValue;
                _this.triggerCallbacks();
            };
            main(resolve, reject);
        }
        Promise.prototype.then = function then(cb) {
            var _this2 = this;
            var next = new Promise(function (resolve) {
                _this2.callbacks.Push(function (x) {
                    return resolve(cb(x));
                });
            });
            return next;
        };
        Promise.prototype.catch = function catch_(cb) {
            var _this2 = this;
            var next = new Promise(function (reject) {
                _this2.callbacks.Push(function (x) {
                    return reject(cb(x));
                });
            });
            return next;
        };
        Promise.prototype.triggerCallbacks = function triggerCallbacks() {
            var _this3 = this;
            this.callbacks.forEach(function (cb) {
                cb(_this3.value);
            });
        };
        return Promise;
    }();
}
12
Amir Forsati

Đây là một ví dụ hoạt động:

const validateName = async userName => {
  const url = "abc/xyz";
  try {
    const response = await axios.get(url);
    return response.data
  } catch (err) {
    return false;
  }
};

validateName("user")
 .then(data => console.log(data))
 .catch(reason => console.log(reason.message))
7
Alex Montoya

Yêu cầu hoạt động theo cách không đồng bộ để bạn không thể đọc dữ liệu theo cách đồng bộ như mã thông thường. Tuy nhiên, bằng cách sử dụng async/await bạn có thể tạo mã async trông gần giống với mã đồng bộ. Mã xử lý dữ liệu yêu cầu cần được bọc bởi chức năng async (load trong đoạn mã bên dưới) và bên trong nó, bạn cần thêm await keywort trước foo() (cũng sử dụng async/await ).

async function foo() {
  var url= 'https://jsonplaceholder.typicode.com/todos/1';
  var result= await (await fetch(url)).text(); // or .json()
  return result;
}

async function load() {
  var result = await foo();
  console.log(result);
}

load();
5
Kamil Kiełczewski

sử dụng async/await với một bộ chuyển mã như Babel để làm cho nó hoạt động trong các trình duyệt cũ hơn. Bạn cũng sẽ phải cài đặt cài đặt sẵn Babel này và polyfill từ npm: npm i -D babel-preset-env babel-polyfill.

function getData(ajaxurl) { 
  return $.ajax({
    url: ajaxurl,
    type: 'GET',
  });
};

async test() {
  try {
    const res = await getData('https://api.icndb.com/jokes/random')
    console.log(res)
  } catch(err) {
    console.log(err);
  }
}

test();

hoặc cuộc gọi lại .then chỉ là một cách khác để viết cùng một logic.

getData(ajaxurl).then(function(res) {
    console.log(res)
}
1
Murtaza Hussain