2012-07-02 40 views
34

Tôi đang sử dụng the Q module cho Node.js để tránh "kim tự tháp doom" trong các trường hợp mà tôi có nhiều bước. Ví dụ:Làm cách nào để hủy bỏ chuỗi lời hứa của node.js bằng Q?

function doTask(task, callback) 
{ 
    Q.ncall(task.step1, task) 
    .then(function(result1){ 
     return Q.ncall(task.step2, task); 
    }) 
    .then(function(result2){ 
     return Q.ncall(task.step3, task); 
    }) 
    .fail(callback).end(); 
} 

Về cơ bản, điều này dường như hiệu quả; nếu một lỗi được ném bởi bất kỳ bước nhiệm vụ nào, nó sẽ được chuyển tới hàm gọi lại (mặc dù tôi sẽ được chào đón để cải tiến, vì tôi là mới đối với các lời hứa của node.js). Tuy nhiên, tôi có một vấn đề khi tôi cần phải hủy bỏ chuỗi nhiệm vụ sớm. Ví dụ, nếu result1 được trả về thành công tôi có thể muốn gọi gọi lại sớm và hủy bỏ phần còn lại, nhưng những nỗ lực của tôi để làm như vậy được không ...

function doTask(task, callback) 
{ 
    Q.ncall(task.step1, task) 
    .then(function(result1){ 
     if(result1) 
     {// the rest of the task chain is unnecessary 
      console.log('aborting!'); 
      callback(null, result1); 
      return null; 
     } 
     return Q.ncall(task.step2, task); 
    }) 
    .then(function(result2){ 
     console.log('doing step 3...'); 
     return Q.ncall(task.step3, task); 
    }) 
    .fail(callback).end(); 
} 

Trong ví dụ này, tôi thấy cả hai "hủy!" và "đang thực hiện bước 3 ..." được in.

Tôi chắc chắn rằng tôi chỉ hiểu lầm một số nguyên tắc cơ bản ở đây, vì vậy sẽ đánh giá cao bất kỳ trợ giúp nào. Cảm ơn!

+0

Một giải pháp tôi thấy là để tạo ra một chuỗi lời hứa riêng biệt sau chuỗi đầu tiên có thể phá vỡ. Hơn nữa, trong ví dụ trên, câu lệnh .then với result2 sẽ được gắn với Q.ncall cho bước 2, thay vì được gắn với lời hứa ban đầu. BAO GIỜ, nhược điểm chính ở đây là nó được loại bỏ một trong những lợi ích chính cho Q theo ý kiến ​​của tôi: tránh các kim tự tháp của doom! Nó vẫn còn tốt hơn không hứa hẹn gì cả, nhưng tôi không thích giải pháp ... –

Trả lời

16

Bất kỳ lỗi được ném trong chuỗi lời hứa sẽ làm cho toàn bộ ngăn xếp để được hủy bỏ sớm và kiểm soát được trao cho các con đường lỗi trở lại. (trong trường hợp này, trình xử lý fail() Khi bạn phát hiện một trạng thái nào đó khiến bạn muốn hủy bỏ chuỗi lời hứa, thì chỉ cần ném một lỗi rất cụ thể, mà bạn bẫy trong lỗi và bỏ qua (nếu bạn chọn)

function doTask(task, callback) 
{ 
    Q.ncall(task.step1, task) 
    .then(function(result1){ 
     if(result1 == 'some failure state I want to cause abortion') 
     {// the rest of the task chain is unnecessary 
      console.log('aborting!'); 
      throw new Error('abort promise chain'); 
      return null; 
     } 
     return Q.ncall(task.step2, task); 
    }) 
    .then(function(result2){ 
     console.log('doing step 3...'); 
     return Q.ncall(task.step3, task); 
    }) 
    .fail(function(err) { 
     if (err.message === 'abort promise chain') { 
      // just swallow error because chain was intentionally aborted 
     } 
     else { 
      // else let the error bubble up because it's coming from somewhere else 
      throw err; 
     } 
    }) 
    .end(); 
} 
+17

Bạn đang sử dụng ngoại lệ cho luồng điều khiển và điều này thường không được đề xuất. Các giải pháp được đưa ra bởi Kris Kowal tránh được vấn đề đó. –

+3

'return null' là không cần thiết sau khi' ném' – Pepijn

34

Đây là trường hợp bạn sẽ cần nhánh, điều đó có nghĩa là lồng hoặc tạo chương trình con.

function doTask(task, callback) { 
    return Q.ncall(task.step1, task) 
    .then(function(result1) { 
     if (result1) return result1; 
     return Q.ncall(task.step2, task) 
     .then(function(result2) { 
      return Q.ncall(task.step3, task); 
     }) 
    }) 
    .nodeify(callback) 
} 

Hoặc

function doTask(task, callback) { 
    return Q.ncall(task.step1, task) 
    .then(function(result1) { 
     if (result1) { 
      return result1; 
     } else { 
      return continueTasks(task); 
     } 
    }) 
    .nodeify(callback) 
} 

function continueTasks(task) { 
    return Q.ncall(task.step2, task) 
    .then(function(result2) { 
     return Q.ncall(task.step3, task); 
    }) 
} 
+0

Đây có phải là cách tiếp cận tốt nhất cho phân nhánh không? Có vẻ như điều này lại giới thiệu thụt đầu dòng khi có nhiều nhánh. Dưới đây là [ví dụ] (https://gist.github.com/svenjacobs/3f42bbaf4cbabe2b58b5) nơi tôi thực hiện nhiều thao tác tệp bằng [q-io] (https://github.com/kriskowal/q-io). Lần đầu tiên tôi kiểm tra xem có tồn tại một thư mục hay không, liệt kê các tệp tìm kiếm một tệp nhất định và xóa tệp đó nếu chỉ tìm thấy một tệp phù hợp. Có nhiều điều kiện nếu có trong đó nên hủy bỏ chuỗi. Tôi sử dụng một giá trị trả về đặc biệt để kiểm tra trường hợp đó nhưng phải kiểm tra nó trong mọi hàm. Đây có phải là một cách tiếp cận tốt? –

+4

@SvenJacobs những gì bạn mô tả trong ví dụ đó là một trường hợp tốt cho trường hợp ngoại lệ. Hãy xem xét https://gist.github.com/kriskowal/e98774443eb0f1653871 –

+2

Tôi vẫn gặp sự cố với phương pháp này vì nó khiến việc xử lý lỗi trở nên khó khăn hơn. Ném trong một chuỗi lời hứa (câu trả lời của Calvin Alvin) cho phép có một '.fail()' duy nhất sẽ xử lý bất kỳ lỗi nào trong luồng. Viết lời hứa theo cách này (phân nhánh) đưa tôi trở lại địa ngục gọi lại. – Pedro

2

Tôi tin rằng bạn chỉ phải từ chối lời hứa để thoát khỏi chuỗi lời hứa.

https://github.com/kriskowal/q/wiki/API-Reference#qrejectreason

cũng nó có vẻ như END() đã được thay đổi để .done()

function doTask(task, callback) 
{ 
    Q.ncall(task.step1, task) 
    .then(function(result1){ 
     if(result1) 
     {// the rest of the task chain is unnecessary 
      console.log('aborting!'); 
      // by calling Q.reject, your second .then is skipped, 
      // only the .fail is executed. 
      // result1 will be passed to your callback in the .fail call 
      return Q.reject(result1); 
     } 
     return Q.ncall(task.step2, task); 
    }) 
    .then(function(result2){ 
     console.log('doing step 3...'); 
     return Q.ncall(task.step3, task); 
    }) 
    .fail(callback).done(); 
} 
Các vấn đề liên quan