2012-11-19 43 views
25

Tôi đã đấu tranh với điều này trong một thời gian. Tôi mới sử dụng Javascript và đã bị ấn tượng rằng mã tôi đang viết đang chạy không đồng bộ. Đây là ví dụ chung:JavaScript dường như không chờ đợi giá trị trả lại

Tôi chạy một số mã trong hàm a. Hàm A sau đó gọi hàm B, người cần trả về một biến cho A để A có thể sử dụng nó trong các hoạt động sau đó của nó. Dường như khi A gọi B, nó vẫn tiếp tục chạy mã riêng của nó, không chờ đợi bị chặn cho giá trị trả về của nó, và B không đủ nhanh để A kết thúc đạt đến điểm cần thiết để sử dụng trả lại giá trị và tôi nhận được lỗi loại biến không xác định. Cách tôi đã làm việc xung quanh điều này là có chức năng Một cuộc gọi Chức năng B mà sau đó gọi một chức năng C mà sẽ làm những gì các hoạt động sau đó A sẽ làm với giá trị trả lại .... Tôi là loại serializing mã của tôi thông qua các cuộc gọi thay vì lợi nhuận ... đó là cồng kềnh dù ...

Dưới đây là một ví dụ khi nó xảy ra trong mã thực tế:

function initialize() { 
    //Geocode Address to obtin Lat and Long coordinates for the starting point of our map 
    geocoder = new google.maps.Geocoder(); 
    var results = geocode(geocoder); 
    makeMap(results[0].geometry.location.lat(), results[0].geometry.location.lng()); 

} 

function geocode(geocoder) { 
    //do geocoding here... 

    var address = "3630 University Street, Montreal, QC, Canada"; 
    geocoder.geocode({ 'address': address }, function (results, status) { 
     if (status == google.maps.GeocoderStatus.OK) { 
      return results; 
      } 
     else { 
      alert("Geocode was not successful for the following reason: " + status); 
     } 
    }); 

} 

function makeMap(lat, long) { 
    // alert(lat); for debuging 
    var mapOptions = { 
     center: new google.maps.LatLng(lat, long), 
     zoom: 17, 
     mapTypeId: google.maps.MapTypeId.ROADMAP 
    }; 
    map = new google.maps.Map(document.getElementById("map_canvas"), 
     mapOptions); 
} 

Lưu ý: khởi tạo được gọi bởi onload cơ thể = "initialize()" trong html của tôi.

Vì vậy, vấn đề là makeMap yêu cầu giá trị vĩ độ và kinh độ thu được bởi hàm Geocode, nhưng tôi gặp lỗi trong bảng điều khiển cho biết kết quả là không xác định. Chuyện gì vậy? Tôi đến từ Java vì vậy tôi là một chút bối rối về cách lưu lượng dữ liệu đang xảy ra ở đây trong JS! Đây sẽ là những bài học quý giá cho tương lai!

Về một câu hỏi phụ: Tôi nên chia các chức năng của mình ra các tập lệnh ngoài như thế nào? Những gì được coi là thực hành tốt? nên tất cả các chức năng của tôi được nhồi nhét vào một tập tin .js bên ngoài hoặc tôi nên nhóm như các chức năng với nhau?

+3

Được rồi, cảm ơn, tôi mới ở đây! –

Trả lời

31

Bạn dường như có một sự hiểu biết tốt của vấn đề, nhưng nó có vẻ như bạn không quen với cách giải quyết nó. Cách phổ biến nhất để giải quyết vấn đề này là sử dụng gọi lại. Về cơ bản, đây là cách không đồng bộ để chờ một giá trị trả về. Dưới đây là cách bạn có thể sử dụng trong trường hợp của mình:

function initialize() { 
    //Geocode Address to obtin Lat and Long coordinates for the starting point of our map 
    geocoder = new google.maps.Geocoder(); 
    geocode(geocoder, function(results) { 
     // This function gets called by the geocode function on success 
     makeMap(results[0].geometry.location.lat(), results[0].geometry.location.lng());   
    }); 
} 

function geocode(geocoder, callback) { 
    //do geocoding here... 

    var address = "3630 University Street, Montreal, QC, Canada"; 
    geocoder.geocode({ 'address': address }, function (results, status) { 
     if (status == google.maps.GeocoderStatus.OK) { 
      // Call the callback function instead of returning 
      callback(results); 
     } else { 
      alert("Geocode was not successful for the following reason: " + status); 
     } 
    }); 

} 

... 
11

Tôi ... đã có ấn tượng rằng mã tôi đang viết đang chạy không đồng bộ.

Vâng, đúng vậy. Chức năng geocodecủa bạn không thể trả về kết quả của cuộc gọi đến API Google, bởi vì hàm trả về trước khi cuộc gọi Google hoàn tất. Xem ghi chú bên dưới:

function geocode(geocoder) { 
    //do geocoding here... 

    var address = "3630 University Street, Montreal, QC, Canada"; 
    geocoder.geocode({ 'address': address }, function (results, status) { 
     if (status == google.maps.GeocoderStatus.OK) { 
      // +---------- This doesn't return anything from your 
      // v   geocode function, it returns a value from the callback 
      return results; 
      } 
     else { 
      alert("Geocode was not successful for the following reason: " + status); 
     } 
    }); 
} 

để thay thế, bạn phải mã hàm của mình để gọi khi có kết quả. Ví dụ .:

// Added a callback arg ---v 
function geocode(geocoder, callback) { 
    //do geocoding here... 

    var address = "3630 University Street, Montreal, QC, Canada"; 
    geocoder.geocode({ 'address': address }, function (results, status) { 
     if (status == google.maps.GeocoderStatus.OK) { 
      // v---------- Call the callback 
      callback(results); 
      } 
     else { 
      alert("Geocode was not successful for the following reason: " + status); 
      callback(null); // <--- Call the callback with a flag value 
          // saying there was an error 
     } 
    }); 
} 

Sau đó, thay vì sử dụng nó như thế này:

var results = geocode(someArgHere); 
if (results) { 
    doSomething(results); 
} 
else { 
    doSomethingElse(); 
} 

Bạn gọi nó là như thế này:

geocode(someArgHere, function() { 
    if (results) { 
     doSomething(results); 
    } 
    else { 
     doSomethingElse(); 
    } 
}); 

Ví dụ: bạn đi đầy đủ không đồng bộ.

+0

không phải là những gì mà hàm ẩn danh (có hai kết quả tham số, trạng thái) đang thực hiện? –

+0

@GeorgesKrinker: Có, bạn có thể chuyển 'callback' trực tiếp tới Google nếu bạn không cần chuyển đổi kết quả trước khi trả lại mã gọi. –

1

Câu lệnh trả về bên trong hàm ẩn danh trả về từ hàm ẩn danh, không phải từ hàm mã địa lý bên ngoài. Hàm mã địa lý trả về không xác định. Phương thức geocoder.geocode có thể gọi hàm ẩn danh bất cứ khi nào nó muốn, đồng bộ hóa hoặc không đồng bộ. Kiểm tra tài liệu cho nó.

1

Thật vậy, bạn đúng khi nhận ra rằng các cuộc gọi không đồng bộ và bạn không nhận được một giá trị trả lại thích hợp.

Thông thường, khi các hàm được gọi bằng j, chúng được đồng bộ.

e.g. a() calls b(), and a() waits until b() to finish before continuing. 

Tuy nhiên, trong một số trường hợp, chẳng hạn như thực hiện cuộc gọi jj hoặc jsonp, nó được thực hiện không đồng bộ. Đây chính xác là những gì đang xảy ra khi bạn gọi geocode().

thi của bạn:

initialize() is called; 
initialize() calls geocoder(); 
geocoder makes a request to Google, and returns null in the meantime. 
initialze() calls makemap() 
the Google geocoder returns at some point, and executed the success callback, which you have defined as "return results;", but there is nothing to return, since the function has already ended. 

Vì vậy, cụ thể, sử dụng gọi lại mà đã được xây dựng vào cuộc gọi geocoder:

if (status == google.maps.GeocoderStatus.OK) { 
    makeMap(results); 
} 
+0

Điều này cũng có vẻ xảy ra nếu thay vì gọi đến dịch vụ bên ngoài (như cuộc gọi async trên google), tôi lặp qua một mảng và trả về mảng vì tôi cần nó ... tại sao vậy? –

+0

Điều đó phải đồng bộ. Bạn có thể tái tạo điều này trong một [jsFiddle] (http://jsfiddle.net) không? –

+0

Ok Tôi không chắc chắn làm thế nào để sử dụng nhưng http://jsfiddle.net/gpLYH/1/ Nơi mà vấn đề xảy ra là khi loadData() gọi addMarkers() sau khi cố gắng để retreave dữ liệu từ getStations() mà nên đã trả về một mảng với các trạm ... –

Các vấn đề liên quan