2014-04-28 12 views
40

Tôi đang gặp phải sự cố nhỏ khi trả về một giá trị từ hàm gọi lại trong Node.js, tôi sẽ cố gắng giải thích tình huống của mình dễ dàng nhất có thể. Hãy xem xét tôi có một đoạn, trong đó có URL và truy cập url đó và cho kết quả:Trả về một giá trị từ hàm gọi lại trong Node.js

urllib.request(urlToCall, { wd: 'nodejs' }, function (err, data, response) {        
    var statusCode = response.statusCode; 
    finalData = getResponseJson(statusCode, data.toString()); 
}); 

Tôi cố gắng để quấn nó bên trong một hàm và trả về một giá trị như thế này:

function doCall(urlToCall) { 
urllib.request(urlToCall, { wd: 'nodejs' }, function (err, data, response) {        
    var statusCode = response.statusCode; 
    finalData = getResponseJson(statusCode, data.toString()); 
    return finalData; 
}); 
} 

Bởi vì trong tôi đang Node.js, tôi có rất nhiều if-else tuyên bố nơi giá trị của urlToCall sẽ được quyết định, như thế này:

if(//somecondition) { 
    urlToCall = //Url1; 
} else if(//someother condition) { 
    urlToCall = //Url2; 
} else { 
    urlToCall = //Url3; 
} 

Vấn đề là tất cả các câu lệnh bên trong a urllib.request sẽ giữ nguyên, ngoại trừ giá trị urlToCall. Vì vậy, chắc chắn tôi cần phải đặt những mã phổ biến bên trong một chức năng. Tôi đã thử tương tự nhưng trong doCall sẽ luôn trả lại cho tôi undefined. Tôi đã cố gắng như thế này:

response = doCall(urlToCall); 
console.log(response) //Prints undefined 

Nhưng nếu tôi in giá trị bên trong doCall() nó in một cách hoàn hảo, nhưng nó sẽ luôn luôn trở undefined. Theo nghiên cứu của tôi, tôi đã biết rằng chúng tôi không thể trả về các giá trị từ các chức năng gọi lại! (Có đúng không)? Nếu có, bất cứ ai có thể tư vấn cho tôi cách xử lý tình huống này, vì tôi muốn ngăn chặn mã trùng lặp trong mỗi khối if-else.

+0

"là nó có đúng không?" - Vâng chắc chắn. –

+0

@JanDvorak, vì vậy tôi không có bất kỳ tùy chọn nào khác ngoài việc sao chép mã? ;) –

+1

Bạn có thể thực hiện một số trợ giúp gọi lại của riêng mình không? Tôi cũng tin là vậy. –

Trả lời

77

undefined của nó vì, console.log(response) chạy trước khi doCall(urlToCall); kết thúc. Bạn phải chuyển qua chức năng gọi lại, chạy khi yêu cầu của bạn được thực hiện.

Đầu tiên, chức năng của bạn. Vượt qua nó một callback:

function doCall(urlToCall, callback) { 
    urllib.request(urlToCall, { wd: 'nodejs' }, function (err, data, response) {        
     var statusCode = response.statusCode; 
     finalData = getResponseJson(statusCode, data.toString()); 
     return callback(finalData); 
    }); 
} 

Bây giờ là:

var urlToCall = "http://myUrlToCall"; 
doCall(urlToCall, function(response){ 
    // Here you have access to your variable 
    console.log(response); 
}) 

@Rodrigo, đăng tải một good resource trong các ý kiến. Đọc khoảng gọi lại trong nút và cách chúng hoạt động. Hãy nhớ rằng, đó là mã không đồng bộ.

+0

+1 Cảm ơn, ví dụ của bạn có vẻ tốt. Sẽ cố gắng hiểu và thực hiện nó. –

+4

Điều này hoạt động tốt. Nhưng tôi có một câu hỏi. khi gọi hàm doCall, hãy bỏ qua điều đó. Làm thế nào tôi có thể trả về biến trả lời? Nếu ví dụ tôi muốn nhận được biến đó bên ngoài hàm? – Romeo

+0

@Romeo cùng một câu hỏi ở đây !! – himanshupareek66

10

Tôi đang phải đối mặt với rắc rối nhỏ trong việc trả lại một giá trị từ hàm callback trong Node.js

Đây không phải là một "sự cố nhỏ", nó thực sự là không thể "trở lại" một giá trị trong truyền thống ý nghĩa từ một hàm không đồng bộ.

Vì bạn không thể "trả lại giá trị", bạn phải gọi hàm sẽ cần giá trị khi bạn có. @display_name đã trả lời câu hỏi của bạn, nhưng tôi chỉ muốn chỉ ra rằng lợi nhuận trong doCall là không trả lại giá trị theo cách truyền thống. Bạn có thể viết doCall như sau:

function doCall(urlToCall, callback) { 
    urllib.request(urlToCall, { wd: 'nodejs' }, function (err, data, response) {        
     var statusCode = response.statusCode; 
     finalData = getResponseJson(statusCode, data.toString()); 
     // call the function that needs the value 
     callback(finalData); 
     // we are done 
     return; 
    }); 
} 

Dòng callback(finalData); là những gì gọi hàm mà cần các giá trị mà bạn nhận được từ các chức năng async.Nhưng lưu ý rằng câu lệnh return được sử dụng để chỉ ra rằng chức năng kết thúc tại đây, nhưng nó không có nghĩa là giá trị được trả lại cho người gọi (người gọi đã chuyển.)

2

Nếu những gì bạn muốn là làm cho mã của bạn hoạt động mà không sửa đổi quá nhiều. Bạn có thể thử giải pháp này mà được thoát khỏi callbacksgiữ công việc cùng một mã:

Cho rằng bạn đang sử dụng Node.js, bạn có thể sử dụng coco-request để đạt được cùng một mục tiêu mà không cần lo lắng gọi lại.

Về cơ bản, bạn có thể làm một cái gì đó như thế này:

function doCall(urlToCall) { 
    return co(function *(){ 
    var response = yield urllib.request(urlToCall, { wd: 'nodejs' }); // This is co-request.        
    var statusCode = response.statusCode; 
    finalData = getResponseJson(statusCode, data.toString()); 
    return finalData; 
    }); 
} 

Sau đó,

var response = yield doCall(urlToCall); // "yield" garuantees the callback finished. 
console.log(response) // The response will not be undefined anymore. 

Bằng cách này, chúng tôi chờ đợi cho đến khi chức năng gọi lại kết thúc, sau đó lấy giá trị từ nó. Bằng cách nào đó, nó giải quyết vấn đề của bạn.

+0

nhưng đây là linh hồn ecma6 –

+0

và nó cũng ảnh hưởng đến hiệu suất của tôi –

+0

Ecma6 có phải là một vấn đề không? –

0

Ví dụ mã cho Node.js - chức năng async để đồng bộ hóa chức năng:

var deasync = require('deasync'); 

function syncFunc() 
{ 
    var ret = null; 
    asyncFunc(function(err, result){ 
     ret = {err : err, result : result} 
    }); 

    while((ret == null)) 
    { 
     deasync.runLoopOnce(); 
    } 

    return (ret.err || ret.result); 
} 
Các vấn đề liên quan