2016-04-27 19 views
6

Tôi đang sử dụng Node.js và SDK Box. My (! Thất bại) mã trông như thế này:Node.js: Làm thế nào để bạn xử lý các cuộc gọi lại trong một vòng lặp?

var connection = box.getConnection(req.user.login); 
connection.ready(function() { 
    connection.getFolderItems(0, null, function (err, result) { 
    if (err) { 
     opts.body = err; 
    } else { 
     opts.body = result; 
     var a = []; 
     for (var i=0; i < result.entries.length; i++) { 
     connection.getFileInfo(result.entries[i].id, function (err, fileInfo) { 
     if (err) { 
      opts.body = err; 
     } else { 
      a.push(fileInfo); 
     } 
     });} 
    } 

Trong thuật ngữ "thủ tục", đây là mà tôi đang cố gắng để làm:

var connection= box.getConnection() 
var items = connection.getFolderItems() 
var itemList = new List 
foreach (item in items) { 
    connection.getFileInfo(item.id) 
    itemList.add(item) 
} 
display(itemList) 

Vấn đề của tôi là connection.getFolderItems()connection.getFileInfo() là không đồng bộ - vòng lặp "for" thoát trước khi tất cả các kết quả được trả về.

Q: Cách tốt nhất trong Node.js là 1) nhận kết quả của cuộc gọi async đầu tiên, 2) lặp qua danh sách, thực hiện nhiều cuộc gọi không đồng bộ hơn và 3) xử lý kết quả khi mọi thứ được "hoàn thành ".

Q: Có phải promises là lựa chọn tốt ở đây không?

Q: Có phải là done()/next() một tùy chọn không?

Hỏi: Có bất kỳ "thành ngữ chuẩn" nào trong Node.js cho loại kịch bản này không?

Trả lời

8

Promises là một ý tưởng tuyệt vời, nhưng bạn có thể muốn xem xét các mô-đun async, đặc biệt là xử lý bộ sưu tập. Nó cho phép bạn chạy các cuộc gọi không đồng bộ chống lại một danh sách các 'thứ' và cung cấp cho bạn một nơi để chạy một phương thức khi tất cả các cuộc gọi async được thực hiện. Không biết nếu điều này là tốt hơn hơn lời hứa, nhưng các tùy chọn luôn tốt đẹp.

// Should give you a rough idea 
async.each(items, function (item, callback) { 
    connection.getFileInfo(result, callback); 
}, function (err) { 
    console.log('All done'); 
}); 

https://github.com/caolan/async#each

+0

Tất cả các câu trả lời tôi nhận được là tuyệt vời - cảm ơn bạn! Tôi vết thương bằng cách sử dụng async, như bạn và JasonWihardja đề nghị. Đây là một hướng dẫn tốt: http://justinklemm.com/node-js-async-tutorial/ – paulsm4

+0

Tôi vẫn nghĩ rằng hứa hẹn cuối cùng là cách thành ngữ hơn để làm điều này, nhưng tôi vui vì bạn thấy nó hữu ích. – skarface

5

Q: Cách tốt nhất trong Node.js tới 1) có được là kết quả của các cuộc gọi async đầu tiên là gì, 2) lặp qua danh sách, làm cho nhiều cuộc gọi async, và 3) xử lý các kết quả khi mọi thứ đã hoàn tất".

Có nhiều cách tiếp cận. Hand coding, Promises, thư viện Async. "Tốt nhất" là trong mắt của khán giả vì vậy không thực sự cho chúng tôi để nói ở đây. Tôi sử dụng Promises cho tất cả các mã async của tôi. Chúng đã được chuẩn hóa chính xác trong ES6 và có những triển khai tốt, mạnh mẽ (tôi thích Bluebird cho các tính năng bổ sung vượt quá tiêu chuẩn để đơn giản hóa các tác vụ không đồng bộ phức tạp và đó là tính năng promisifyAll() cung cấp cho bạn giao diện lời hứa trên bất kỳ hoạt động async chuẩn nào sử dụng quy ước gọi lại gọi lại async).

Tôi khuyên bạn nên chống lại các hoạt động không đồng bộ hóa mã hóa tay phức tạp vì việc xử lý lỗi mạnh là rất khó và ngoại lệ có thể bị âm thầm ăn trong các cuộc gọi lại không đồng bộ dẫn đến việc xử lý lỗi bị mất và gỡ lỗi khó khăn. Thư viện Async có lẽ là cách không tốt nhất để làm những việc vì nó cung cấp một số tính năng cơ sở hạ tầng và đồng bộ hóa xung quanh các cuộc gọi lại không đồng bộ.

Cá nhân tôi thích lời hứa. Tôi nghĩ rằng chúng ta sẽ thấy nhiều API không đồng bộ hơn tiêu chuẩn hóa về việc trả lời một lời hứa khi thời gian di chuyển về phía trước vì vậy tôi nghĩ rằng đó là lựa chọn tốt hơn cho một cách tìm kiếm và chương trình hướng tới tương lai.

Hỏi: Lời hứa có phải là lựa chọn tốt ở đây không?

Có, hứa hẹn cho phép bạn chạy một loạt các hoạt động không đồng bộ và sau đó sử dụng một cái gì đó như Promise.all() để biết khi nào chúng được thực hiện xong. Nó cũng sẽ thu thập tất cả các kết quả từ tất cả các hoạt động không đồng bộ cho bạn.

Q: Được thực hiện()/tiếp theo() một tùy chọn?

Tôi không chắc chắn bạn đang hỏi gì ở đây, nhưng bạn có thể mã thủ công các hoạt động không đồng bộ để chạy song song và biết thời điểm chúng được thực hiện hoặc chạy tuần tự và biết khi nào chúng được thực hiện. Lời hứa làm rất nhiều công việc này cho bạn, nhưng bạn có thể làm điều đó một cách thủ công mà không có chúng.

Hỏi: Có bất kỳ "thành ngữ chuẩn" nào trong Node.js cho loại kịch bản này không?

Nếu sử dụng lời hứa, sẽ có một cách phổ biến để thực hiện việc này. Nếu không sử dụng lời hứa, có thể không phải là "thành ngữ tiêu chuẩn" vì có nhiều cách khác nhau để tự viết mã.

Thực hiện Promise

Dưới đây là một ví dụ sử dụng thư viện Bluebird Promise trong Node.js:

var Promise = require('bluebird'); 
var connection = Promise.promisifyAll(box.getConnection(req.user.login)); 
connection.ready(function() { 
    connection.getFolderItemsAsync(0, null).then(function(result) { 
     return Promise.map(result.entries, function(item) { 
      return connection.getFileInfoAsync(item.id); 
     }) 
    }).then(function(results) { 
     // array of results here 
    }, function(err) { 
     // error here 
    }); 
}); 

Dưới đây là cách hoạt động:

  1. Promisify đối tượng kết nối để tất cả các phương thức của nó đều có một phiên bản trả về một lời hứa (chỉ cần thêm "Async" vào cuối phương thức để gọi cho prom này phiên bản được hợp nhất).

  2. Gọi getFolderItemsAsync() và lời hứa của mình sẽ giải quyết với result.entries mảng

  3. Run bản đồ về mảng đó, chạy tất cả các hoạt động song song và trả về một lời hứa mà giải quyết với một loạt các kết quả yêu cầu khi tất cả các hoạt động đã xong.

  4. Kết quả thực tế cho từng mục nhập đạt được với connection.getFileInfoAsync().

  5. Tạo trình xử lý xử lý và trình xử lý từ chối. Nếu bất kỳ lỗi nào xảy ra ở bất kỳ nơi nào trong tiến trình, nó sẽ lan truyền tới trình xử lý từ chối. Nếu tất cả các hoạt động thành công, trình xử lý phân giải cuối cùng sẽ được gọi với một mảng kết quả đã sắp xếp.

Phiên bản ở trên sẽ hủy nếu có lỗi và bạn không nhận được kết quả nào ngoài mã lỗi. Nếu bạn muốn tiếp tục với một kết quả null nếu có một lỗi, sau đó bạn có thể sử dụng một cái gì đó như thế này:

var Promise = require('bluebird'); 
var connection = Promise.promisifyAll(box.getConnection(req.user.login)); 
connection.ready(function() { 
    connection.getFolderItemsAsync(0, null).then(function(result) { 
     return Promise.map(result.entries, function(item) { 
      return connection.getFileInfoAsync(item.id).catch(function(err){ 
       // post the results as null and continue with the other results 
       return null; 
      }); 
     }) 
    }).then(function(results) { 
     // array of results here (some might be null if they had an error) 
    }, function(err) { 
     // error here 
    }); 
}); 

thủ Coded Version

Dưới đây là một phiên bản bằng tay được mã hóa. Chìa khóa cho điều này là phát hiện khi vòng lặp không đồng bộ của bạn được thực hiện bằng cách so sánh if (results.length === result.entries.length). Lưu ý: Điều này có xử lý lỗi không đầy đủ, đó là một trong những khó khăn của việc mã hóa điều này bằng tay và không sử dụng khung công tác không đồng bộ như lời hứa.

var connection = box.getConnection(req.user.login); 
connection.ready(function() { 
    connection.getFolderItems(0, null, function (err, result) { 
     if (err) { 
      // do error handling here 
      opts.body = err; 
     } else { 
      var results = []; 
      for (var i = 0; i < result.entries.length; i++) { 
       connection.getFileInfo(result.entries[i].id, function (err, fileInfo) { 
        if (err) { 
         // do error handling here 
         opts.body = err; 
         results.push(null); 
        } else { 
         results.push(fileInfo); 
        } 
        // if done with all requests 
        if (results.length === result.entries.length) { 
         // done with everything, results are in results 
         // process final results here 
        } 
       }); 
      } 
     } 
    }); 
}); 
4

Q: Cách tốt nhất trong Node.js tới 1) có được là kết quả của các cuộc gọi async đầu tiên là gì, 2) lặp qua danh sách, làm cho nhiều cuộc gọi async, và 3) quá trình kết quả khi mọi thứ được "thực hiện".

Bạn có thể sử dụng thư viện async hoặc khuyến khích gọi hàm và sử dụng Lời hứa thay thế. Cả hai đều dễ sử dụng.

Hỏi: Lời hứa có phải là lựa chọn tốt ở đây không?

Có. Nhưng nó đòi hỏi bạn phải promisify phương pháp của bạn đầu tiên trước khi sử dụng.

Q: Được thực hiện()/tiếp theo() một tùy chọn?

Từ những gì tôi hiểu, đó là một khái niệm hoàn toàn khác. Các done ở đây đề cập đến một chức năng gọi lại mà bạn có thể gọi sau khi phương pháp kết thúc. Và next thường được sử dụng để thể hiện yêu cầu tới tuyến đường tiếp theo, điều tôi nghĩ không liên quan đến câu hỏi bạn đang yêu cầu.

Hỏi: Có bất kỳ "thành ngữ chuẩn" nào trong Node.js cho loại kịch bản này không?

Nó thường được gọi chỉ "async" hoặc "không chặn" gọi

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