2012-11-04 37 views
34

Tôi chắc chắn rằng sự cố của tôi dựa trên sự thiếu hiểu biết về lập trình asynch trong node.js nhưng ở đây sẽ xảy ra.Node.JS: Làm thế nào để chuyển các biến thành các cuộc gọi lại không đồng bộ?

Ví dụ: Tôi có danh sách liên kết tôi muốn thu thập dữ liệu. Khi mỗi yêu cầu asynch trả về, tôi muốn biết URL đó là gì. Tuy nhiên, có lẽ do điều kiện chủng tộc, mỗi yêu cầu sẽ trả về với URL được đặt thành giá trị cuối cùng trong danh sách.

var links = ['http://google.com', 'http://yahoo.com']; 
for (link in links) { 
    var url = links[link]; 
    require('request')(url, function() { 
     console.log(url); 
    }); 
} 

sản lượng dự kiến:

http://google.com 
http://yahoo.com 

đầu ra thực tế:

http://yahoo.com 
http://yahoo.com 

Vì vậy, câu hỏi của tôi là một trong hai:

  1. Làm thế nào để vượt qua url (theo giá trị) đến chức năng gọi lại? HOẶC
  2. Cách thích hợp để ghép các yêu cầu HTTP để chúng chạy tuần tự là gì? HOẶC
  3. Tôi còn thiếu cái gì khác?

PS: Đối với 1. Tôi không muốn một giải pháp kiểm tra thông số của cuộc gọi lại mà là cách tổng quát gọi lại các biến 'từ phía trên'.

Trả lời

46

Biến số url của bạn không được chuyển sang vòng lặp for vì JavaScript chỉ hỗ trợ phạm vi toàn cầu và chức năng. Vì vậy, bạn cần phải tạo ra một phạm vi chức năng cho request cuộc gọi của bạn để nắm bắt những giá trị url trong mỗi lần lặp của vòng lặp bằng cách sử dụng một chức năng ngay lập tức:

var links = ['http://google.com', 'http://yahoo.com']; 
for (link in links) { 
    (function(url) { 
     require('request')(url, function() { 
      console.log(url); 
     }); 
    })(links[link]); 
} 

BTW, nhúng một require ở giữa vòng lặp là không tốt thực hành. Nó có lẽ nên được viết lại là:

var request = require('request'); 
var links = ['http://google.com', 'http://yahoo.com']; 
for (link in links) { 
    (function(url) { 
     request(url, function() { 
      console.log(url); 
     }); 
    })(links[link]); 
} 
+3

Đó không phải là phạm vi. Đó là về đóng cửa. Rất nhiều ngôn ngữ khác không có phạm vi chặn nhưng không phải đối mặt với vấn đề này do thiếu đóng cửa. – slebetman

+3

Vấn đề là nếu JavaScript hỗ trợ phạm vi khối, thì quyền truy cập đóng cửa vào 'url' trong hàm gọi lại của mã OP sẽ hoạt động vì mỗi lần lặp của vòng lặp sẽ nhận biến' url' riêng của nó (như trong C#). – JohnnyHK

+1

Yêu cầu chỉ trong đó ngắn gọn, trong mã của tôi tất cả yêu cầu là lúc đầu. – Marc

8

Xem https://stackoverflow.com/a/11747331/243639 để thảo luận chung về vấn đề này.

tôi muốn đề nghị một cái gì đó giống như

var links = ['http://google.com', 'http://yahoo.com']; 

function createCallback(_url) { 
    return function() { 
     console.log(_url); 
    } 
}; 

for (link in links) { 
    var url = links[link]; 
    require('request')(url, createCallback(url)); 
} 
+0

Có rất nhiều liên kết tốt hơn để đưa vào StackOverflow để giải thích vấn đề. Ví dụ này: http://stackoverflow.com/questions/3572480/please-explain-the-use-of-javascript-closures-in-loops/3572616#3572616 – slebetman

8

Kiểm tra blog ra này. Một biến có thể được truyền bằng cách sử dụng phương thức .bind(). Trong trường hợp của bạn, nó sẽ như sau:

var links = ['http://google.com', 'http://yahoo.com']; 
for (link in links) { 
var url = links[link]; 

require('request')(url, function() { 

    console.log(this.urlAsy); 

}.bind({urlAsy:url})); 
} 
+1

Tôi thích phương pháp này nhiều hơn vì nó ít tiết kiệm hơn gói một cái gì đó trong một chức năng. –

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