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

5743
Felix Kling 2013-01-09 07:06.

Tôi có một hàm footạo yêu cầu không đồng bộ. Làm cách nào để trả lại phản hồi / kết quả từ đó foo?

Tôi đã thử trả lại giá trị từ lệnh gọi lại, cũng như gán kết quả cho biến cục bộ bên trong hàm và trả về biến đó, nhưng không có cách nào trong số đó thực sự trả về phản hồi (tất cả đều trả về undefinedhoặc bất kể giá trị ban đầu của biến resultlà gì) .

Ví dụ sử dụng ajaxhàm của jQuery :

function foo() {
    var result;

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

    return result; // It always returns `undefined`
}

Ví dụ sử dụng node.js:

function foo() {
    var result;

    fs.readFile("path/to/file", function(err, data) {
        result = data;
        // return data; // <- I tried that one as well
    });

    return result; // It always returns `undefined`
}

Ví dụ sử dụng thenkhối của một lời hứa:

function foo() {
    var result;

    fetch(url).then(function(response) {
        result = response;
        // return response; // <- I tried that one as well
    });

    return result; // It always returns `undefined`
}

30 answers

5905
Felix Kling 2013-01-09 07:06.

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

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

Vấn đề

Các Một trong Ajax là viết tắt của Asynchronous . Điều đó có nghĩa là việc gửi yêu cầu (hoặc đúng hơn là nhận phản hồi) được đưa ra khỏi quy trình thực thi thông thường. Trong ví dụ của bạn, $.ajaxtrả 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 successgọi lại thậm chí được gọi.

Đây là một phép tương tự hy vọng làm cho sự khác biệt giữa luồng đồ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 cho một người bạn và yêu cầu anh ấy tìm kiếm thông tin gì đó cho bạn. Mặc dù có thể mất một chút thời gian, nhưng bạn hãy đợ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 lệnh 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ù findItemcó thể mất nhiều thời gian để thực thi, bất kỳ mã nào đến sau var item = findItem();phải đợi cho đến khi hàm trả về kết quả.

Không đồng bộ

Bạn gọi lại cho bạn mình 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 qua điện thoại di động. Bạn gác máy, rời khỏi nhà và làm bất cứ điều gì bạn đị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 mà anh ấy đã cung cấp cho bạn.

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

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

Thay vì đợi 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 hiện. Để cuối cùng nhận được phản hồi, bạn cung cấp một hàm sẽ được gọi khi nhận được phản hồi, một lệnh gọi lại (thông báo đ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 hiện trước khi lệnh gọi lại được gọ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" cũng vậy), nhưng thường không khuyến khích sử dụng chúng, đặc biệt là trong ngữ cảnh trình duyệt.

Bạn hỏi tại sao nó xấu?

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

Tất cả những điều này là một trải nghiệm người dùng thực sự tồi tệ. 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, ảnh hưởng sẽ tồi tệ hơn đối với người dùng có kết nối chậm.

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

  • Hứa hẹn vớiasync/await (ES2017 +, có sẵn trong các trình duyệt cũ hơn nếu bạn sử dụng trình 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ớithen() (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 vào 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 / hiểu hơn.

async/awaitxây dựng dựa trên các lời hứa: một asynchàm luôn trả về một lời hứa. await"mở ra" một lời hứa và kết quả là giá trị mà lời hứa đã được giải quyết hoặc tạo ra một lỗi nếu lời hứa bị từ chối.

Quan trọng: Bạn chỉ có thể sử dụng awaitbên trong một asynchàm. Hiện tại, cấp cao nhất awaitchưa được hỗ trợ, vì vậy bạn có thể phải tạo IIFE không đồng bộ ( Biểu thức hàm được gọi ngay lập tức ) để bắt đầu một asyncngữ cảnh.

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

Dưới đây là một ví dụ dựa trên sự chậm 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);
})();

Hỗ trợ các phiên bản trình duyệtnút hiện tại 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 trình 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 ).


Cho phép các hàm chấp nhận các lệnh gọi lại

Một lệnh gọi lại là khi hàm 1 được chuyển cho hàm 2. Hàm 2 có thể gọi hàm 1 bất cứ khi nào nó sẵn sàng. Trong bối cảnh của một quá trình không đồng bộ, lệnh 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 cho lệnh gọi lại.

Trong ví dụ của câu hỏi, bạn có thể foochấp nhận một cuộc gọi lại và sử dụng nó như một successcuộc gọi lại. Vì vậy, điều nà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 "inline" nhưng bạn có thể chuyển bất kỳ tham chiếu hàm 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
    });
}

callbacksẽ tham chiếu đến hàm mà chúng ta chuyển đến fookhi chúng ta gọi nó và chúng ta chuyển nó vào success. Tức là sau khi yêu cầu Ajax thành công, $.ajaxsẽ gọi callbackvà chuyển phản hồi đến lệnh gọi lại (có thể được gọi là với result, vì đây là cách chúng tôi định nghĩa lệnh gọi lại).

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

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

Viết mã bằng lệnh gọi lại dễ dàng hơn tưởng tượng. Rốt cuộc, JavaScript trong trình duyệt chủ yếu hướng sự kiện (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 bằng cách chỉ cần suy nghĩ thông qua luồng ứng dụng.


ES2015 +: Hứa hẹn với sau đó ()

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

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

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

Đây là một ví dụ 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).
  });

Được á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ư sau:

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ả những 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. Chúng cung cấp sự trừu tượng và tách biệt tuyệt vời cho mã của bạn.

Thông tin thêm về lời hứa: HTML5 stone - JavaScript Promises

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

Đối tượng trả chậm là gì? là triển khai tùy chỉnh của jQuery đối với các hứa hẹn (trước khi API Hứa hẹn được chuẩn hóa). Chúng hoạt động gần giống như những lời hứa nhưng hiển thị một API hơi khác.

Mọi phương thức Ajax của jQuery đã trả về một "đối tượng được hoãn lại" (thực sự là một lời hứa của một đối tượng được hoãn lại) mà bạn chỉ 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
});

Ghi chú bên: Lời hứa gotchas

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

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()mã không đóng băng trong khi kiểm tra trang '/ password' trên máy chủ của bạn - nó gửi một yêu cầu đến máy chủ và trong khi chờ đợi, nó ngay lập tức trả về một đối tượng jQuery Ajax Deferred, không phải phản hồi từ máy chủ. Điều đó có nghĩa là ifcâu lệnh sẽ luôn lấy đối tượng Deferred này, xử lý nó truevà tiến hành như thể người dùng đã đăng nhập. Không tốt.

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

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 khuyến nghị: Lệnh 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 vì lợi ích hoàn chỉnh, đây là cách bạn thực hiện một lệnh gọi đồng bộ:

Không có jQuery

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

jQuery

Nếu bạn sử dụng jQuery , bạn có thể đặt asynctùy chọn thành false. Lưu ý rằng tùy chọn này không được dùng nữa kể từ jQuery 1.8. Sau đó, bạn vẫn có thể sử dụng lệnh successgọi lại hoặc truy cập thuộc responseTexttính 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, $.getJSONv.v., bạn phải thay đổi nó thành $.ajax(vì bạn chỉ có thể chuyển các tham số cấu hình đến $.ajax).

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

1096
Benjamin Gruenbaum 2013-05-30 13:30.

Nếu bạn không sử dụng jQuery trong mã của mình, 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 sử dụng.

( Lưu ý, đối với những người sử dụng fetchAPI, Angular hoặc các hứa hẹn mới , 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à bản tóm tắt ngắn gọn của "Giải thích vấn đề" từ câu trả lời kia, nếu bạn không chắc chắn sau khi đọc cái này, hãy đọc cái đó.

Các Một trong AJAX là viết tắt của Asynchronous . Điều đó có nghĩa là việc gửi yêu cầu (hay đúng hơn là nhận phản hồi) được đưa ra khỏi quy trình thực thi bình thường. Trong ví dụ của bạn, .sendtrả 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 successgọi lại thậm chí được gọi.

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

Đây là một phép loại suy đơn giản

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

(Vĩ cầm)

Giá trị được atrả về là undefineddo a=5phần chưa được thực thi. AJAX hoạt động như vậ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à kích hoạt lại mã , 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);
}

Đây được gọi là CPS . Về cơ bản, chúng tôi đang chuyển getFivemột hành động để thực hiện khi nó hoàn thành, chúng tôi đang nói với mã của mình cách phản ứng khi một sự kiện hoàn thành (như lệnh 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à sẽ cảnh báo "5" trên màn hình. (Chơi đùa) .

Phương pháp khả thi

Về cơ bản có hai cách để giải quyết vấn đề 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 bình thường với các lệnh gọi lại.

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

Còn đối với AJAX đồng bộ thì không nên! Câu trả lời của Felix đưa ra một số lý lẽ thuyết phục về lý do tại sao đó là một ý tưởng tồi. Tóm lại, nó sẽ đóng băng trình duyệt của người dùng cho đến khi máy chủ trả về 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.

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 các vấn đề nghiêm trọng ...

Nếu bạn phải làm điều đó, bạn có thể vượt qua một cờ: Đây là cách thực hiện:

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ã

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

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 tôi đã truyền một hàm ẩn danh, nhưng chúng tôi 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 loại thiết kế gọi lại này được thực hiện, hãy xem câu trả lời của Felix.

Bây giờ, hãy xác định chính foo để hành động cho 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 tất 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 nếu trạng thái phản hồi không phải là 200 và hoạt động tương ứng (tạo trình xử lý thất bại và tương tự). 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 hướng dẫn bắt đầu AJAX tại MDN.

411
cocco 2013-08-19 22:06.

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 có một XMLHttpRequest 2 ngắn gọn đẹp mắt 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. 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 onload 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 khó chịu.

Có hai cách để nhận 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ì lý do nào đó bạn bind()gọi lại một lớp:

e.target.response

Thí dụ:

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

Hoặc (cái ở trên tốt hơn là các hàm ẩn danh 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 nên sử dụng onreadystatechange hoặc thậm chí là tên biến XMLHttpRequest. Sai rồi.

Kiểm tra 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 phương pháp này vì XMLHttpRequest 2 tồn tại. Tôi chưa bao giờ gặp bất kỳ loại sự cố nào trên tất cả các trình duyệt mà tôi sử dụng.

onreadystatechange chỉ hữu ích nếu bạn muốn lấy tiêu đề ở trạng thái 2.

Sử dụng XMLHttpRequesttên biến là một lỗi lớn khác vì bạn cần thực hiện lệnh gọi lại bên trong các lần đóng onload / oreadystatechange nếu không bạn đã đánh 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 post 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 ... nó là một hàm rất ngắn, nhưng nó có được và đă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 chuyển một phần tử biểu mẫu đầ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 triển khai đồ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 nhận xét, việc sử dụng error && sync hoàn toàn phá vỡ quan điểm của câu trả lời. Cách ngắn gọn hay để sử dụng Ajax theo cách thích hợp là gì?

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 tập lệnh trên, bạn có một trình xử lý lỗi được định nghĩa 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ự thoát ra lỗi, cách duy nhất là viết sai URL trong trường hợp đó mọi trình duyệt đều gặp 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 responseType thành bộ đệm mảng blob hoặc bất cứ thứ gì ...

Ngay cả khi bạn chuyển 'POSTAPAPAP' làm phương thức, nó sẽ không gây ra lỗi.

Ngay cả khi bạn chuyển 'fdggdgilfdghfldj' làm dữ liệu biểu mẫu, nó sẽ không xuất hiện lỗi.

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

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

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.typecái được đặt thành lỗi.

Tại sao phải thêm trình xử lý lỗi nếu bạn hoàn toàn không có quyền kiểm soát lỗi? Hầu hết các lỗi được trả về bên trong hàm 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à bài kiểm tra đầu tiên tôi viết x ('x', displayAjax) ..., và nó hoàn toàn nhậ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 tên là 'x.xml'. Vì vậy, ngay cả khi bạn quên phần mở rộng của tệp XMLHttpRequest 2 SẼ TÌM HIỂU . Tôi LOL'd


Đọc đồng bộ tệp

Đừ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 .txtđồng bộ tệp lớn tốt đẹp .

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 các API hoặc chỉ các tệp trong danh sách của riêng bạn hoặc bất cứ thứ 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 mà bạn luôn tải cùng một XML / JSON hoặc bất cứ thứ gì bạn chỉ cần một chức năng. 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 sử dụng cơ bản.

Nếu bạn muốn MỞ RỘNG chức năng ...

Có, 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 được nhiều thứ với XMLHttpRequest 2:

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

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

326
Benjamin Gruenbaum 2015-05-12 16:22.

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 (có hoãn lại), thay thế của XHR gốc (tìm nạp), EmberJS, lưu của BackboneJS hoặc bất kỳ thư viện nút nào trả về các hứa hẹn.

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 lệnh gọi lại cho AJAX. Tôi có 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 trên giao diện người dùng 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ủ với 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 thentrình xử lý luôn được thực thi không đồng bộ - nghĩa là sau đoạn mã bên dưới chúng mà không có trong .thentrình xử lý.

Điều này có nghĩa khi bạn trở về datacác thenhandler bạn đã xác định không thực hiện được nêu ra. Điều này có nghĩa là giá trị bạn đang trả về đã không được đặt thành giá trị chính xác trong thời gian.

Đây là một phép 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 dataundefineddata = 5phần chưa được thực thi. Nó có thể sẽ thực thi trong một giây nhưng tại thời điểm đó nó không liên quan đến giá trị trả về.

Vì hoạt động chưa xảy ra (AJAX, lệnh gọi máy chủ, IO, bộ đếm thời gian) bạn đang trả về 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à kích hoạt lại mã , 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. Những lời hứa tích cực kích hoạt điều này bằng cách tự nhiên (nhạy cảm với thời gian).

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 . Lời hứa có trạng thái, chúng bắt đầu như đang chờ xử lý không có giá trị và có thể giải quyết thành:

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

Một lời hứa chỉ có thể thay đổi trạng thái một lần sau đó nó sẽ luôn ở trạng thái cũ mãi mãi. Bạn có thể đính kèm các thentrình xử lý vào các lời hứa để trích xuất giá trị của chúng và xử lý lỗi. thentrình xử lý cho phép Không phải những lời hứa chỉ là những cuộc gọi lại? các cuộc gọi. Lời hứa được tạo ra bằng Làm cách nào để chuyển đổi API gọi lại hiện có thành các hứa hẹn? . Ví dụ: thay thế AJAX hiện đại hơn fetchhoặc $.getlời hứa trả lại của jQuery .

Khi chúng ta yêu cầu .thenmột lời hứa và trả lại một cái gì đó từ nó - chúng ta sẽ 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ữ lấy con ngựa của chúng ta.

Với những lời hứa

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. Đầu tiên, chúng ta hãy chứng minh sự hiểu biết của chúng ta về các trạng thái hứa từ trên bằng cách sử dụng hàm tạo Promise để tạo một 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 các lời hứa, chúng tôi có thể sử dụng thenđể làm cho nó có giá trị:

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ả lại một giá trị mà chúng tôi không thể làm được vì mô hình đồng thời - chúng tôi trả lại một wrapper cho một giá trị mà chúng ta có thể unwrap với then. Nó giống như một chiếc hộp bạn có thể mở bằng then.

Áp dụng cái này

Điều này giống 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 cũng hoạt động. Chúng tôi đã biết rằng chúng tôi không thể trả về các giá trị từ các lệnh gọi đã không đồng bộ nhưng chúng tôi có thể sử dụng các lời hứa và chuỗi chúng để thực hiện xử lý. Bây giờ chúng ta biết cách trả lại phản hồi từ một cuộc gọi không đồng bộ.

ES2015 (ES6)

ES6 giới thiệu các trình tạo là các chức năng có thể quay trở lại ở giữa và sau đó tiếp tục lại đ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 trình lặp trên chuỗi 1,2,3,3,3,3,....có thể được lặp lại. Trong khi điều này tự nó là thú vị và mở ra nhiều khả năng có một trường hợp thú vị đặc biệt.

Nếu trình tự mà chúng tôi đang 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 tạo ra và đợi nó trước khi chúng tôi tiếp tục chức năng. Vì vậy, thay vì một dãy số, chúng ta cần một dãy giá trị tương lai - đó là: lời hứa.

Thủ thuật hơi 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ố "người chạy" 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 Bluebird's Promise.coroutineở đây, nhưng có những trình bao bọc khác như cohoặ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 thức này trả về chính một lời hứa mà chúng ta có thể sử dụng từ các coroutines 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 tiêu 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ể awaithứa. Đây chỉ là "đường" (cú pháp đẹp hơn) cho đề xuất ES6 ở trên bằng cách thêm asyncawaittừ khóa. 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ả về một lời hứa như cũ :)

256
Nic 2014-05-23 16:05.

Bạn đang sử dụng Ajax không đúng cách. Ý tưởng không phải là để nó trả về bất cứ thứ gì, mà thay vào đó, chuyển dữ liệu cho một thứ gọi là hàm gọi lại, hàm này 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ứ thứ gì trong trình xử lý gửi sẽ không làm được gì cả. Thay vào đó, bạn phải xử lý dữ liệu hoặc làm những gì bạn muốn với nó ngay bên trong hàm thành công.

242
Hemant Bavle 2014-02-19 08:58.

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

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);    
}); 
229
Johannes Fahrenkrug 2016-08-12 04:17.

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

165
Maleen Abewardana 2014-08-26 22:11.

Angular1

Đố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,

Prom Promise có thể được sử dụng để hủy hợp nhất 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ũng có thể tìm thấy một lời giải thích hay ở đây .

Ví dụ được tìm thấy trong các tài liệu được đề cập bên dưới.

  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 trở lên

Trong Angular2với hãy xem ví dụ sau, nhưng nó được khuyến khích sử dụng Observablesvớ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ể sử dụng nó 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ài gốc tại đây. Nhưng Typecript không hỗ trợ các Promise 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à thông số kỹ thuật hứa hẹn được xác định ở đây.

159
T.J. Crowder 2017-05-04 06:59.

Hầu hết các câu trả lời ở đây đều đưa ra các gợi ý hữu ích khi bạn có một thao tác không đồng bộ, 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 nhập trong một mảng hoặc cấu trúc giống 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 lệnh gọi lại từ doSomethingAsyncvẫn chưa chạy vào thời điểm 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 một danh sách nào đó) và muốn thực hiện các thao tác không đồng bộ cho mỗi mục nhập, 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 theo chuỗi (nối tiếp nhau theo thứ tự).

Song song, tương đông

Bạn có thể bắt đầu tất cả chúng và theo dõi số lần gọi lại mà bạn đang mong đợi, sau đó sử dụng kết quả khi bạn nhận được nhiều lệnh 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ỏ expectingvà chỉ sử dụng results.length === theArray.length, nhưng điều đó khiến chúng tôi mở ra khả năng theArraybị 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 indextừ forEachđể lưu kết quả ở resultscùng vị trí với mục nhập mà nó liên quan, ngay cả khi kết quả đến không theo thứ tự (vì các lệnh gọi không đồng bộ không nhất thiết phải hoàn thành theo thứ tự mà chúng được bắt đầu).

Nhưng nếu bạn cần trả lại 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 có hàm của bạn chấp nhận và gọi một cuộc gọi lại (hoặc trả về một Lời hứa ). Đâ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à một phiên bản trả về Promisethay thế:

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 doSomethingAsyncchúng tôi thông qua lỗi, chúng tôi sẽ sử dụng rejectđể từ chối lời hứa khi chúng tôi có 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 để doSomethingAsynctrả về một lời hứa và sau đó thực hiện như bên dưới ...)

Nếu doSomethingAsyncđưa cho bạn một Lời hứa , 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 điều đó doSomethingAsyncsẽ bỏ qua đối số thứ hai và thứ ba, bạn có thể chuyển trực tiếp đối số đó tới map( mapgọi lệnh gọi lại của nó với ba đối số, nhưng hầu hết mọi người chỉ sử dụng phần lớn 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.allgiải quyết lời hứa của nó bằng một loạt các kết quả của tất cả các lời hứa mà bạn đưa ra khi tất cả chúng đã được giải quyết, hoặc từ chối lời hứa khi lời hứa đầu tiên mà bạn đưa ra từ chối.

Loạt

Giả sử bạn không muốn các hoạt động diễn ra song song? Nếu bạn muốn chạy chúng lần lượt, bạn cần đợi từng thao tác hoàn thành trước khi bắt đầu thao tác tiếp theo. Đây là ví dụ về một hàm thực hiện điều đó và gọi một lệnh 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ả không 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 có 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 một trình bao bọc để doSomethingAsynccung cấp cho bạn một lời hứa và thực hiện những điều bên dưới ...)

Nếu doSomethingAsynccung cấp cho bạn một Lời hứa, nếu bạn có thể sử dụng cú pháp ES2017 + (có thể với một trình chuyển tiếp như Babel ), bạn có thể sử dụng một asynchàm 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 Reduce" (điều này phức tạp hơn so với Promise Reduce thông thường vì chúng tôi không chuyển kết quả từ cái này sang cái tiếp theo mà 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 của 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;
}

113
Francisco Carmona 2016-06-02 22:31.

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 getJoketrả về một lời hứa đã giải quyết (nó được giải quyết khi quay trở lại res.data.value). Vì vậy, bạn đợi cho đến khi yêu cầu $ http.get được 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);
  });
})();
112
Alireza 2017-05-24 23:38.

Đây là một trong những nơi mà hai cách liên kết dữ liệu hoặc khái niệm lưu trữ được sử dụng trong nhiều khung JavaScript mới sẽ hoạt động 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 có hai cách liên kết dữ liệu hoặc lưu trữ khái niệm , vấn đề này chỉ được khắc phục cho bạn, vì vậy, nói một cách dễ hiểu, kết quả của bạn là undefinedở giai đoạn đầu tiên, vì vậy bạn có result = undefinedtrước khi nhận được dữ liệu, sau đó ngay 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 mà phản hồi của lệnh gọi Ajax của bạn ...

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

Bạn có thể sử dụng một lệnh gọi lại , lời hứacó thể quan sát được gần đây để xử lý nó cho bạn, ví dụ: trong các lời hứa, chúng tôi có một số chức năng như success()hoặc then()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 chức năng gọi lại hoặc đăng ký trên có thể quan sát .

Ví dụ, trong trường hợp bạn đang sử dụng jQuery , bạn có thể làm như sau:

$(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ề các hứa hẹnkhả năng quan sát là những cách mới hơn để thực hiện nội dung không đồng bộ này.

105
Anish K. 2017-11-01 10:12.

Đó là một vấn đề rất phổ biến mà chúng tôi gặp phải khi đấu tranh với những '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 xong công việc của nó' trước dòng tiếp theo trong chuỗi) và kết quả giống như mong đợi.

Bây giờ chúng ta hãy thêm một chút xoắn, 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 'kết thúc' 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ậy là xong, sự chậm trễ đó đã phá vỡ chức năng mà chúng tôi mong đợi! Nhưng chính xác thì điều gì đã xảy ra? Chà, nó thực sự khá hợp lý nếu bạn nhìn vào mã. hàm foo(), khi thực thi, không trả về gì (do đó giá trị 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 bar là thứ được trả về ngay lập tức từ foo (), không có nghĩa là chỉ undefined.

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

Hãy yêu cầu chức năng của chúng tôi cho một LỜI HỨA . Promise thực sự là về ý nghĩa của nó: nó có nghĩa là hàm đảm bảo bạn cung cấp bất kỳ đầu ra nào mà nó nhận được trong tương lai. vì vậy chúng ta 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'
});

Vì vậy, tóm tắt là - để giải quyết các hàm không đồng bộ như lệnh gọi dựa trên ajax, v.v., bạn có thể sử dụng một lời hứa cho resolvegiá trị (mà bạn định trả về). Vì vậy, 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 hẹn với async / await)

Ngoài việc sử dụng then/catchđể làm việc với các lời hứa, còn tồn tại một cách tiếp cận khác. Ý tưởng là nhận ra một chức năng không đồng bộ và sau đó đợi các 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à ẩn promises, 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 saveUsers(){
     getUsers()
      .then(users => {
         saveSomewhere(users);
      })
      .catch(err => {
          console.error(err);
       })
 }

phiên bản async / await:

  async function saveUsers(){
     try{
        let users = await getUsers()
        saveSomewhere(users);
     }
     catch(err){
        console.error(err);
     }
  }
101
jsbisht 2015-09-03 02:54.

Một cách tiếp cận khác để trả về giá trị từ mộ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 resultđối tượng để 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 công việc không đồng bộ.

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

89
rohithpr 2016-01-26 07:43.

Mặc dù những 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 những điều như sau:

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

Bạn sẽ trải qua async1; kiểm tra xem có phải namelà không 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ù không sao trong những ví dụ nhỏ nhưng nó sẽ gây khó chịu khi bạn gặp phải nhiều trường hợp tương tự và việc 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 .

88
loretoparisi 2016-04-13 12:55.

Ví dụ sau đây tôi đã viết cho thấy cách

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

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

Bối cảnh. Ví dụ đang truy vấn điểm cuối API Web Spotify để tìm kiếm playlistcác đối tượng cho một tập hợp 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, một Lời hứa mới sẽ kích hoạt một khối - ExecutionBlock, phân tích cú pháp kết quả, lập lịch cho một bộ lời hứa mới dựa trên mảng kết quả, đó là danh sách các userđối tượng Spotify và thực hiện lệnh gọi HTTP mới trong ExecutionProfileBlockkhông đồng bộ.

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

LƯU Ý Các searchAPI Spotify 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, để chạy ví dụ sau, bạn cần đặt mã thông báo truy cập của mình trong 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 Cách tìm kiếm người dùng theo tên bằng API Web Spotify .

84
Pablo Matias Gomez 2016-04-23 04:47.

Câu trả lời ngắn gọn là, bạn phải triển khai một lệnh gọi lại như sau:

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

$.ajax({
    url: "...",
    success: callback
});
82
mikemaccana 2017-06-02 23:51.

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' , điều này sẽ cho JavaScript biết chờ đợi lời hứa được phân giải thành một giá trị (như phản hồi HTTP)
  • Thêm từ khóa 'async' vào hàm mẹ

Đây là phiên bản mã của bạn đang hoạt động:

(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

80
Aniket Jha 2018-02-03 20:06.

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 sự kiện là nơi tất cả chức năng của bạn được đẩy trên một số sự kiện (ví dụ: nhấp chuột), đây là từng người một được thực hiện trong hàng đợi và đưa vào Vòng lặp sự kiện thực thi chức năng này và chuẩn bị cho chính nó cho cái tiếp theo sau khi cái đầu tiên được thực thi Điều này có nghĩa là Việc thực thi một hàm không bắt đầu cho đến khi hàm trước khi nó trong 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, một 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 rồi đến hàm useiseData (). Hàm serverRequest đi trong 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 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 dự kiến ​​sẽ mất thời gian và do đó chúng tôi bận rộn vòng lặp sự kiện của mình, do đó trang của chúng tôi bị treo, đó là nơi Web API phát huy vai trò của nó, nó lấy chức năng này từ vòng lặp sự kiện và xử lý 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 hàm tiếp theo từ hàng đợi. Hàm tiếp theo trong hàng đợi là useiseData () đi trong vòng lặp nhưng do không có sẵn dữ liệu nên nó hoạt động lãng phí và việc 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à gọi Async tức là chúng ta có thể làm điều gì đó khác cho đến khi nhận được dữ liệu)

Giả sử hàm serverRequest () của chúng tôi có câu lệnh trả về trong một đoạn mã, khi chúng tôi lấy lại dữ liệu từ máy chủ Web API sẽ đẩy nó vào hàng đợi ở cuối hàng đợi. Vì nó được đẩ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ừ Async Call.

Vì vậy, Giải pháp cho điều này là gọi lại hoặc lời hứa .

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

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

Gọi lại Javscript.info

70
Vinoth Rajendran 2016-05-27 03:26.

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)
});
70
amaksr 2017-05-27 16:47.

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 quảng bá

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

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 quảng bá

Bước 1. Gói hàm với callback vào trình bao bọc nhận biết nsynjs (nếu nó có phiên bản quảng bá, 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 một cách đồ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à biểu thức theo 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.

Các ví dụ khác tại đây: https://github.com/amaksr/nsynjs/tree/master/examples

42
James 2018-02-18 05:26.

ECMAScript 6 có 'trình 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 đoạn mã trên, bạn làm như sau:

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.

Lệnh gọi lại ...argsđược bao bọc 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 lệnh 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);
39
Mohan Dere 2016-08-13 23:36.

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 Lời hứa trình duyệt
  2. Q - Thư viện hứa hẹn dành cho JavaScript
  3. A + Promises.js
  4. jQuery bị hoãn lại
  5. API XMLHttpRequest
  6. Sử dụng khái niệm gọi lại - Như triển khai trong câu trả lời đầu tiên

Ví dụ: triển khai jQuery trì hoãn để 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();

38

Chúng ta thấy mình đang ở trong một vũ trụ dường như đang tiến triển dọc theo một 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 các từ vựng và khái niệm trừu tượ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 mà chúng tôi xây dựng - ngày càng nhiều - có thời gian là một thứ quan trọng. Một số điều chắc chắn sẽ xảy ra trong tương lai. Sau đó, những thứ 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à "tính không đồng bộ". Trong thế giới ngày càng được nối mạng của chúng ta, trường hợp không đồng bộ phổ biến nhất là chờ một hệ thống từ xa nào đó phản hồi một số yêu cầu.

Hãy xem xét một ví dụ. Bạn gọi người bán sữa và gọi một ít sữa. Khi nó đến, bạn muốn cho nó vào cà phê của mình. Bạn không thể cho sữa vào cà phê của mình ngay bây giờ vì nó chưa có ở đây. Bạn phải đợi nó đến trước khi cho nó vào cà phê của bạn. Nói cách khác, những điều sau 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 phải chờ đợi cho order_milkđến khi kết thúc trước khi nó thực thi put_in_coffee. Nói cách khác, nó không biết rằng đó order_milkkhông đồng bộ - là thứ sẽ không tạo ra 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 hiện hết câu lệnh này đến 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, tận dụng lợi thế của thực tế là JS hỗ trợ các hàm như các đối tượng lớp đầu tiên có thể được truyền xung quanh, là truyền một hàm dưới dạng 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_milkbắt đầu, đặt hàng sữa, sau đó, khi và chỉ khi nó đến, nó gọi put_in_coffee.

Vấn đề với phương pháp gọi lại này là nó gây ô nhiễm ngữ nghĩa bình 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 lệnh gọi lại đã cho 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 cho vào cà phê, sau đó và chỉ sau đó thực hiện bước thứ ba, đó là uống cà phê. Cuối cùng tôi 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 đang chuyển tới put_in_coffeecả sữa để cho vào nó và cả hành động ( drink_coffee) để thực thi khi sữa đã được đưa vào. Đoạn mã như vậy 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", là một loại giá trị cụ thể đại diện cho một số loại kết quả tương lai hoặc không đồng bộ . 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. Các lời hứa có một phương thức duy nhất, được đặt tên then, mà bạn chuyển một hành động sẽ được thực hiện 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để gửi lại một lời hứa cho sữa đến, sau đó chỉ định put_in_coffeenhư một thenhành động, 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 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 những lời hứa cho vấn đề cụ thể của bạn. Chúng tôi sẽ bọc logic yêu cầu của mình bên trong một hàm, hàm này 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 đã làm là thêm a returnvào cuộc gọi tới $.ajax. Điều này hoạt động vì jQuery $.ajaxđã trả về một thứ giống như lời hứa. (Trong thực tế, nếu không đi sâu vào chi tiết, chúng tôi muốn kết thúc cuộc gọi này để trả lại một lời hứa thực sự hoặc sử dụng một số thay thế cho $.ajaxđiều đó.) Bây giờ, nếu chúng ta muốn tải tệp và đợi nó kết thúc và sau đó làm điều gì đó, chúng ta chỉ có thể nói

get_data() . then(do_something)

ví dụ,

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

Khi sử dụng các hứa hẹn, chúng ta sẽ chuyển 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 hữu ích:

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

các asynctừ khóa

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

a();
b();

nhưng nếu alà không đồng bộ, với các 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 phải đợi cuộc gọi đầu tiên kết thúc trước khi thực hiện cuộc gọi thứ hai". Nó sẽ không được tốt đẹp nếu có một số cách để nói với JS đó? Hóa ra là có - awaittừ khóa, được sử dụng bên trong một loại hàm đặc biệt được gọi là hàm "async". Tính năng này là một phần của phiên bản sắp tới của ES nhưng đã có sẵn trong các bộ chuyển đổi như Babel với các cài đặt trước phù hợp. Điều này cho phép chúng tôi viết đơn giản

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 có thể viết một cái gì đó như

async function foo() {
  data = await get_data();
  console.log(data);
}
37
David R Tribble 2015-09-24 12:52.

Câu trả lời ngắn gọn : foo()Phương thức của bạn trả về ngay lập tức, trong khi lệnh $ajax()gọi thực thi không đồng bộ sau khi hàm trả về . Sau đó, vấn đề là làm thế nào hoặc ở đâu để lưu trữ các kết quả được truy xuất bởi lệnh gọi không đồng bộ 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à truyền một đối tượng cho foo()phương thức và lưu trữ kết quả trong một thành viên của đối tượng đó sau khi hoàn tất lệnh gọi không đồng bộ.

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 tới foo()vẫn sẽ không trả về kết quả nào hữu ích. Tuy nhiên, kết quả của cuộc gọi không đồng bộ bây giờ sẽ được lưu trữ trong result.response.

36
Mahfuzur Rahman 2017-04-24 22:09.

Sử dụng một callback()chức năng bên trong foo()thành công. 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();
30
Amir Fo 2018-12-08 04:10.

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 chờ đã ...!

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 Lời hứa 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 các trình biên dịch ES3 đến bên dưới js nếu nó không được định nghĩa. Chỉ cần thêm mã này trước mã chính của bạn và sau đó sử dụng Promise một cách an toà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;
    }();
}
29
Pieter Jan Bonestroo 2018-01-14 09:13.

Câu hỏi là:

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

có thể được hiểu là:

Làm thế nào để làm cho mã không đồng bộ trông đồ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 Promisesasync / 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, nhưng tôi thích viết nó bằng Python và biên dịch nó sang Javascript bằng Transcrypt . Nó sẽ đủ rõ ràng.)

Trước tiên hãy cho phép kích hoạt sử dụng JQuery, có $sẵn như S:

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

Xác định một hàm trả về một Lời hứa , trong trường hợp này là một 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 mã không đồng bộ như thể nó đồng bộ :

async def readALot():
    try:
        result1 = await read("url_1")
        result2 = await read("url_2")
    except Exception:
        console.warn("Reading a lot failed")
28
Khoa Bui 2017-07-06 10:28.

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

function foo() {
    var result;

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

    return result;
}

function myCallback(response) {
    // Does something.
}
26
SanjiMika 2020-01-20 12:23.

Sau khi đọc tất cả các câu trả lời ở đây và với kinh nghiệm của mình, tôi muốn tiếp tục chi tiết về callback, promise and async/awaitlập trình không đồng bộ trong JavaScript.

1) Gọi lại: Lý do cơ bản cho việc gọi lại là chạy mã để phản hồi một sự kiện (xem ví dụ bên dưới). Chúng tôi sử dụng callback trong JavaScript mọi lúc.

const body = document.getElementsByTagName('body')[0];
function callback() {
  console.log('Hello');
}
body.addEventListener('click', callback);

Nhưng nếu bạn phải sử dụng nhiều lệnh gọi lại lồng nhau trong ví dụ dưới đây, sẽ rất khủng khiếp cho việc tái cấu trúc mã.

asyncCallOne(function callback1() {
  asyncCallTwo(function callback2() {
    asyncCallThree(function callback3() {
        ...
    })
  })
})

2) Promise: một cú pháp ES6 - Promise giải quyết vấn đề địa ngục gọi lại!

const myFirstPromise = new Promise((resolve, reject) => {
  // We call resolve(...) when what we were doing asynchronously was successful, and reject(...) when it failed.
  // In this example, we use setTimeout(...) to simulate async code. 
  // In reality, you will probably be using something like XHR request or an HTML5 API.
  setTimeout(() => {
    resolve("Success!")  // Yay! Everything went well!
  }, 250)
}) 

myFirstPromise
  .then((res) => {
    return res.json();
  })
  .then((data) => {
    console.log(data);
  })
  .catch((e) => {
    console.log(e);
  });

myFirstPromise là một phiên bản Promise đại diện cho quá trình mã không đồng bộ. Hàm giải quyết báo hiệu rằng phiên bản Promise đã kết thúc. Sau đó, chúng ta có thể gọi .then () (một chuỗi .then như bạn muốn) và .catch () trên cá thể lời hứa:

then — Runs a callback you pass to it when the promise has fulfilled.
catch — Runs a callback you pass to it when something went wrong.

3) Async / Await: một cú pháp mới ES6 - Await về cơ bản là cú pháp đường của Promise!

Hàm async cung cấp cho chúng ta một cú pháp ngắn gọn và rõ ràng cho phép chúng ta viết ít mã hơn để đạt được kết quả giống như những gì chúng ta sẽ nhận được với những lời hứa. Async / Await trông tương tự như mã đồng bộ và mã đồng bộ dễ đọc và viết hơn nhiều. Để bắt lỗi với Async / Await, chúng ta có thể sử dụng khối try...catch. Ở đây, bạn không cần phải viết chuỗi .then () của cú pháp Promise.

const getExchangeRate = async () => {
  try {
    const res = await fetch('https://getExchangeRateData');
    const data = await res.json();
    console.log(data);
  } catch (err) {
    console.error(err);
  }
}

getExchangeRate();

Kết luận: Đây hoàn toàn là ba cú pháp cho lập trình không đồng bộ trong JavaScript mà bạn chắc chắn phải hiểu rõ. Vì vậy, nếu có thể, tôi khuyên bạn nên sử dụng "promise" hoặc "async / await" để cấu trúc lại mã không đồng bộ của mình (chủ yếu là cho các yêu cầu XHR) !

20
Matthew Brent 2018-05-05 05:56.

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

Vòng lặp sự kiện và mô hình đồng tiền

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

Nói một cách đơn giản, rộng rãi, vòng lặp sự kiện giống như trình 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();
}

Khi nó nhận được một thông báo để 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à mọi thứ JS cần thực thi để thực hiện lệnh trong thông báo. Vì vậy, trong ví dụ của chúng tôi, nó được yêu cầu gọifoobarFunc

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

Vì vậy, bất kỳ thứ gì 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 đó bị quên - vòng lặp sự kiện sau đó sẽ chuyển sang điều tiếp theo trong hàng đợi (hoặc lắng nghe thông báo)

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

KHI nào thứ gì đó sẽ chạy

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

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ỉ đợi thông báo đó một cách đồng bộ, ứng dụng của bạn sẽ bị đóng băng và nó sẽ rất tệ. Vì vậy, JS tiếp tục thực thi mục tiếp theo trong hàng đợi trong khi chờ thông báo được thêm trở lại 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ứ được gọi là gọi lại . Nó giống như một lời hứa theo nghĩa đen. Như trong tôi hứa sẽ trả lại một cái gì đó tại một số thời điểm jQuery sử dụng các lệnh gọi lại cụ thể được gọi deffered.done deffered.faildeffered.always(trong số những người khác). Bạn có thể xem tất cả ở đây

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

Bởi vì một lệnh gọi lại không được thực thi ngay lập tức nhưng sau đó, điều quan trọng là phải chuyển tham chiếu đến hàm chứ không phải hàm đượ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ẽ fookhông vượt quafoo()

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

Related questions

MORE COOL STUFF

Cate Blanchett chia tay chồng sau 3 ngày bên nhau và vẫn kết hôn với anh ấy 25 năm sau

Cate Blanchett chia tay chồng sau 3 ngày bên nhau và vẫn kết hôn với anh ấy 25 năm sau

Cate Blanchett đã bất chấp những lời khuyên hẹn hò điển hình khi cô gặp chồng mình.

Tại sao Michael Sheen là một diễn viên phi lợi nhuận

Tại sao Michael Sheen là một diễn viên phi lợi nhuận

Michael Sheen là một diễn viên phi lợi nhuận nhưng chính xác thì điều đó có nghĩa là gì?

Hallmark Star Colin Egglesfield Các món ăn gây xúc động mạnh đối với người hâm mộ tại RomaDrama Live! [Loại trừ]

Hallmark Star Colin Egglesfield Các món ăn gây xúc động mạnh đối với người hâm mộ tại RomaDrama Live! [Loại trừ]

Ngôi sao của Hallmark Colin Egglesfield chia sẻ về những cuộc gặp gỡ với người hâm mộ ly kỳ tại RomaDrama Live! cộng với chương trình INSPIRE của anh ấy tại đại hội.

Tại sao bạn không thể phát trực tuyến 'chương trình truyền hình phía Bắc'

Tại sao bạn không thể phát trực tuyến 'chương trình truyền hình phía Bắc'

Bạn sẽ phải phủi sạch đầu đĩa Blu-ray hoặc DVD để xem tại sao Northern Exposure trở thành một trong những chương trình nổi tiếng nhất của thập niên 90.

Where in the World Are You? Take our GeoGuesser Quiz

Where in the World Are You? Take our GeoGuesser Quiz

The world is a huge place, yet some GeoGuessr players know locations in mere seconds. Are you one of GeoGuessr's gifted elite? Take our quiz to find out!

8 công dụng tuyệt vời của Baking Soda và Giấm

8 công dụng tuyệt vời của Baking Soda và Giấm

Bạn biết đấy, hai sản phẩm này là nguồn điện để làm sạch, riêng chúng. Nhưng cùng với nhau, chúng có một loạt công dụng hoàn toàn khác.

Hạn hán, biến đổi khí hậu đe dọa tương lai của thủy điện Hoa Kỳ

Hạn hán, biến đổi khí hậu đe dọa tương lai của thủy điện Hoa Kỳ

Thủy điện rất cần thiết cho lưới điện của Hoa Kỳ, nhưng nó chỉ tạo ra năng lượng khi có nước di chuyển. Bao nhiêu nhà máy thủy điện có thể gặp nguy hiểm khi các hồ và sông cạn kiệt?

Quyên góp tóc của bạn để giúp giữ nước sạch của chúng tôi

Quyên góp tóc của bạn để giúp giữ nước sạch của chúng tôi

Tóc tỉa từ các tiệm và các khoản quyên góp cá nhân có thể được tái sử dụng như những tấm thảm thấm dầu và giúp bảo vệ môi trường.

Đây là Tổng thống Trump đe dọa 'trợ cấp' xe điện của GM vì đóng cửa nhà máy

Đây là Tổng thống Trump đe dọa 'trợ cấp' xe điện của GM vì đóng cửa nhà máy

Trump và Giám đốc điều hành GM Mary Barra vào năm 2017. Kể từ khi tin tức nổ ra ngày hôm qua rằng General Motors đang cắt giảm việc làm ở Bắc Mỹ để chuẩn bị cho tương lai điện / xe tự hành / khủng khiếp có thể xảy ra của chúng tôi, gần như tất cả mọi người đã tự hỏi: Tổng thống Trump, người đã vận động tranh cử trên một nền tảng sẽ như thế nào của việc mang lại và duy trì việc làm ô tô của Mỹ, phải nói gì về điều này? Bây giờ chúng ta biết.

Bản vá lỗi tiếp theo của Fallout 76 sẽ mang lại một số bản sửa lỗi cần thiết

Bản vá lỗi tiếp theo của Fallout 76 sẽ mang lại một số bản sửa lỗi cần thiết

Bethesda cho biết ngày hôm nay trên Reddit, Fallout 76 sẽ có một bản vá lớn khác vào ngày 4 tháng 12, sẽ bắt đầu cố gắng khắc phục một số vấn đề lớn hơn của trò chơi. Ngoài ra, công ty cho biết họ có kế hoạch giao tiếp nhiều hơn trong tương lai về những gì nhóm Fallout 76 đang làm việc và khi nào các bản cập nhật mới cho trò chơi sẽ ra mắt.

Mạng lưới bệnh viện Công giáo sẽ không cho phép nhân viên phá thai tại phòng khám ngoài giờ làm việc

Mạng lưới bệnh viện Công giáo sẽ không cho phép nhân viên phá thai tại phòng khám ngoài giờ làm việc

Bạn có biết rằng một trong sáu bệnh nhân ở bệnh viện ở Hoa Kỳ được điều trị tại một cơ sở Công giáo? Và bạn có biết rằng quyền sở hữu của Công giáo hạn chế sâu sắc việc thiết lập các thủ tục y tế có thể được thực hiện trong các bệnh viện này, rõ ràng nhất là khi nói đến chăm sóc sức khỏe sinh sản? Bây giờ NPR báo cáo rằng sự gia tăng của các bệnh viện thuộc Công giáo đang gây khó khăn hơn cho các bác sĩ — những người bị cấm bởi các quy định do Hoa Kỳ viết.

Khói từ đám cháy rừng ở California đã đến tận thành phố New York

Khói từ đám cháy rừng ở California đã đến tận thành phố New York

Đường chân trời San Francisco bị che khuất bởi khói và khói mù do cháy rừng phía sau Đảo Alcatraz, Thứ Tư, ngày 14 tháng 11 năm 2018, ở San Francisco.

Nicky Hilton Forced to Borrow Paris' 'I Love Paris' Sweatshirt After 'Airline Loses All [My] Luggage'

Nicky Hilton Forced to Borrow Paris' 'I Love Paris' Sweatshirt After 'Airline Loses All [My] Luggage'

Nicky Hilton Rothschild's luggage got lost, but luckily she has an incredible closet to shop: Sister Paris Hilton's!

Kate Middleton dành một ngày bên bờ nước ở London, cùng với Jennifer Lopez, Julianne Hough và hơn thế nữa

Kate Middleton dành một ngày bên bờ nước ở London, cùng với Jennifer Lopez, Julianne Hough và hơn thế nữa

Kate Middleton dành một ngày bên bờ nước ở London, cùng với Jennifer Lopez, Julianne Hough và hơn thế nữa. Từ Hollywood đến New York và mọi nơi ở giữa, hãy xem các ngôi sao yêu thích của bạn đang làm gì!

17 tuổi bị đâm chết trong khi 4 người khác bị thương trong một cuộc tấn công bằng dao trên sông Wisconsin

17 tuổi bị đâm chết trong khi 4 người khác bị thương trong một cuộc tấn công bằng dao trên sông Wisconsin

Các nhà điều tra đang xem xét liệu nhóm và nghi phạm có biết nhau trước vụ tấn công hay không

Thanh thiếu niên, Gia đình Florida Hội đồng quản trị trường học về Luật 'Không nói đồng tính': 'Buộc chúng tôi tự kiểm duyệt'

Thanh thiếu niên, Gia đình Florida Hội đồng quản trị trường học về Luật 'Không nói đồng tính': 'Buộc chúng tôi tự kiểm duyệt'

Vụ kiện, nêu tên một số học khu, lập luận rằng dự luật "Không nói đồng tính" được ban hành gần đây của Florida "có hiệu quả im lặng và xóa bỏ học sinh và gia đình LGBTQ +"

Đường băng hạ cánh

Đường băng hạ cánh

Cuối hè đầu thu là mùa hoài niệm. Những chiếc đèn đường chiếu ánh sáng của chúng qua những con đường đẫm mưa, và những chiếc lá dưới chân - màu đỏ cam tắt trong bóng chạng vạng - là lời nhắc nhở về những ngày đã qua.

Hãy tưởng tượng tạo ra một chiến lược nội dung thực sự CHUYỂN ĐỔI. Nó có thể.

Hãy tưởng tượng tạo ra một chiến lược nội dung thực sự CHUYỂN ĐỔI. Nó có thể.

Vào năm 2021, tôi khuyến khích bạn suy nghĩ lại mọi thứ bạn biết về khách hàng mà bạn phục vụ và những câu chuyện bạn kể cho họ. Lùi lại.

Sự mất mát của voi ma mút đã mở ra trái tim tôi để yêu

Sự mất mát của voi ma mút đã mở ra trái tim tôi để yêu

Vào ngày sinh nhật thứ 9 của Felix The Cat, tôi nhớ về một trong những mất mát lớn nhất trong cuộc đời trưởng thành của tôi - Sophie của tôi vào năm 2013. Tôi đã viết bài luận này và chia sẻ nó trên nền tảng này một thời gian ngắn vào năm 2013.

Khi bạn không thể trở thành người mà Internet muốn bạn trở thành

Khi bạn không thể trở thành người mà Internet muốn bạn trở thành

Tôi ghét từ "tàu đắm". Mọi người cảm thấy thoải mái trong la bàn đạo đức của riêng mình, và khi làm như vậy, họ thấy mình vượt qua sự phán xét.

Language