2015-02-27 15 views
8

câu chuyện ngắn:Làm thế nào để từ chối (và sử dụng đúng) Lời hứa?

  • Nói về Promises/A +, cách thích hợp để từ chối một lời hứa là gì - ném một lỗi? Nhưng nếu tôi bỏ lỡ catch - toàn bộ ứng dụng của tôi sẽ thổi!

  • Cách sử dụng promisify và lợi ích của nó (có thể bạn sẽ cần phải đọc phiên bản dài hơn)?

  • Có phải .then(success, fail) thực sự là anti-pattern và tôi có nên luôn sử dụng .then(success).catch(error) không?

dài hơn phiên bản, được mô tả như vấn đề thực tế đời sống (sẽ yêu một ai đó để đọc):

Tôi đã một thư viện có sử dụng Bluebird (A + Promise thư viện thực hiện), để lấy dữ liệu từ cơ sở dữ liệu (nói khoảng Sequelize). Mỗi truy vấn trả về một kết quả, nhưng đôi khi nó trống (cố gắng chọn một cái gì đó, nhưng không có bất kỳ). Lời hứa đi vào chức năng result, vì không có lý do gì cho lỗi (không có kết quả không phải là lỗi). Ví dụ:

Entity.find(1).then(function(result) { 
    // always ending here, despite result is {} 
}).catch(function(error) { 
    // never ends here 
}); 

Tôi muốn bọc phần này và kiểm tra xem kết quả có trống không (không thể kiểm tra điều này ở mọi nơi trong mã của tôi). Tôi đã làm điều này:

function findFirst() { 
    return Entity.find(1).then(function(result) { 
    if (result) { // add proper checks if needed 
     return result; // will invoke the success function 
    } else { 
     // I WANT TO INVOKE THE ERROR, HOW?! :) 
    } 
    }).catch(function(error) { 
    // never ends here 
    }); 
} 

findFirst().then(function(result) { 
    // I HAVE a result 
}).catch(function(error) { 
    // ERROR function - there's either sql error OR there is no result! 
}); 

Nếu bạn vẫn ở bên tôi - tôi hy vọng bạn sẽ hiểu điều gì đang xảy ra. Tôi muốn bằng cách nào đó để cháy lên các chức năng lỗi (nơi "ERROR chức năng" là). Vấn đề là - tôi không biết làm thế nào. Tôi đã thử những điều sau:

this.reject('reason'); // doesn't work, this is not a promise, Sequelize related 
return new Error('reason'); // calls success function, with error argument 
return false; // calls success function with false argument 
throw new Error('reason'); // works, but if .catch is missing => BLOW! 

Như bạn có thể thấy bằng nhận xét của tôi (và mỗi thông số), việc ném lỗi hoạt động tốt. Tuy nhiên, có một số lớn nhưng - nếu tôi bỏ lỡ câu lệnh .catch, toàn bộ ứng dụng của tôi sẽ phát sinh.

Tại sao tôi không muốn điều này? Giả sử tôi muốn tăng bộ đếm trong cơ sở dữ liệu của mình. Tôi không quan tâm đến kết quả - tôi chỉ yêu cầu HTTP .. Vì vậy, tôi có thể gọi incrementInDB(), có khả năng trả về kết quả (ngay cả đối với lý do kiểm tra), vì vậy có throw new Error nếu không thành công. Nhưng vì tôi không quan tâm đến phản hồi, đôi khi tôi sẽ không thêm câu lệnh .catch, phải không? Nhưng bây giờ - nếu tôi không (về mục đích hoặc do lỗi) - tôi kết thúc với ứng dụng nút của bạn xuống.

Tôi không tìm thấy điều này rất đẹp. Có cách nào tốt hơn cách để làm việc đó hoặc tôi chỉ phải giải quyết nó?

Một người bạn của tôi đã giúp tôi ra và tôi đã sử dụng một lời hứa mới để sửa chữa mọi thứ lên, như thế này:

function findFirst() { 
    var deferred = new Promise.pending(); // doesnt' matter if it's Bluebird or Q, just defer 
    Entity.find(1).then(function(result) { 
    if (result) { // add proper checks if needed 
     deferred.resolve(result); 
    } else { 
     deferred.reject('no result'); 
    } 
    }).catch(function(error) { 
    deferred.reject('mysql error'); 
); 

    return deferred.promise; // return a promise, no matter of framework 
} 

trình như một say mê! Nhưng Tôi đã nhận được điều này: Promise Anti Patterns - bài viết wiki được viết bởi Petka Antonov, tác giả của Bluebird (A + triển khai). Rõ ràng là đây là sai.

Câu hỏi thứ hai của tôi là - có phải vậy không? Nếu đúng thì tại sao? Và cách tốt nhất là gì?

Cảm ơn rất nhiều vì đã đọc bài này, tôi hy vọng ai đó sẽ dành thời gian để trả lời cho tôi :) Tôi nên thêm rằng tôi không muốn phụ thuộc quá nhiều vào khung công tác, vì vậy SequelizeBluebird chỉ là những thứ tôi đã kết thúc làm việc với. Vấn đề của tôi là với Promises như một toàn cầu, không phải với các khung công tác đặc biệt này.

+1

Để 'Nhưng, có một lớn nhưng - nếu tôi bỏ lỡ tuyên bố .catch, toàn bộ ứng dụng của tôi thổi lên.' không phải là những gì ngoại lệ là về? Bạn nên luôn luôn xử lý ngoại lệ, ngay cả khi bạn không quan tâm đến sự thất bại, bạn nên làm cho rõ ràng trong mã bằng cách thêm một '.catch' với một nhận xét mà bạn không quan tâm về nó. Hãy tưởng tượng bạn sẽ xem lại mã của bạn một lần nữa và nhận ra rằng có một ngoại lệ không được chấp nhận nhưng bạn không nhớ tại sao nó lại không được giải quyết, sau đó bạn sẽ dành nhiều thời gian để xác định lý do. –

+0

Đó là về năng suất, làm việc với nhiều nhà phát triển và không phải ở độ tin cậy cuối cùng. Nó dễ dàng hơn cho bất kỳ dev nào để viết 'incrementInDB(); 'hơn là lướt mã, đọc đây là lời hứa sẽ báo lỗi, vì vậy bạn ** phải bắt nó.Như tôi đã nói - Tôi không muốn ngoại lệ - tôi chỉ không thể tìm ra cách khác để làm điều đó mà không làm nổ tung toàn bộ ứng dụng của tôi :) –

+1

Tôi nghĩ nhận xét của t.niese phải là câu trả lời. Có, nó có thể được * dễ dàng hơn * cho bất kỳ dev để viết một dòng mã hơn để viết * mã * thích hợp, nhưng đó không phải là lý do. Cách quá thường xuyên tôi thấy mã từ các nhà phát triển thiếu kinh nghiệm, nơi ngoại lệ được ném từ một số API được âm thầm nuốt và chuyển đổi để trở về null vv Một số nhà phát triển dường như có một số loại sợ hãi cơ bản của trường hợp ngoại lệ. Với việc sử dụng đúng cách catch() và thực hiện() Tôi thực sự không thấy bất kỳ vấn đề nào. Bên cạnh đó, bạn luôn có thể viết xử lý ngoại lệ không bị bắt buộc của riêng mình nếu cần? – JHH

Trả lời

5

Nói về lời hứa/A +, cách thích hợp để từ chối lời hứa - đưa ra lỗi là gì? Nhưng nếu tôi bỏ lỡ vụ bắt - toàn bộ ứng dụng của tôi sẽ nổ tung!

Và nếu bạn sử dụng mã trả lại để báo lỗi thay vì ngoại lệ, việc thiếu kiểm tra sẽ gây ra lỗi tinh vi và hành vi thất thường. Quên để xử lý lỗi là một lỗi, nhưng việc ứng dụng của bạn bị nổ tung trong trường hợp không may như vậy là more often better hơn là để cho nó tiếp tục ở trạng thái bị hỏng.

Gỡ lỗi kiểm tra mã lỗi bị quên chỉ rõ ràng bởi ứng dụng hoạt động lạ ở một số địa điểm không liên quan đến nguồn lỗi là dễ dàng nhiều đơn đặt hàng có cường độ đắt hơn gỡ lỗi một lần bị lãng quên vì bạn gặp lỗi thông báo, nguồn lỗi và theo dõi ngăn xếp. Vì vậy, nếu bạn muốn có năng suất trong kịch bản phát triển ứng dụng điển hình, ngoại lệ sẽ giành được lợi nhuận khá lớn.

.Sau đó (thành công, thất bại)

Điều này thường là báo hiệu rằng bạn đang sử dụng những lời hứa callbacks như tôn vinh nơi callbacks chỉ đơn giản là thông qua cách riêng biệt. Điều này rõ ràng là một mô hình chống như bạn sẽ không nhận được bất kỳ lợi ích từ việc sử dụng lời hứa theo cách này. Nếu đó không phải là trường hợp, thậm chí sau đó bạn chỉ làm cho mã của bạn hơi ít có thể đọc được so với sử dụng .catch(), tương tự như cách method(true, true, false) ít dễ đọc hơn method({option1: true, option2: true, option3: false}).

Cách sử dụng quảng cáo và lợi ích của nó (có thể bạn sẽ cần phải đọc phiên bản dài hơn)?

Hầu hết các mô-đun chỉ hiển thị giao diện gọi lại, tự động chuyển giao giao diện gọi lại thành giao diện lời hứa. Nếu bạn đang viết mã như vậy bằng tay (sử dụng hoãn lại hoặc new Promise), bạn đang lãng phí thời gian của mình.

Câu hỏi thứ hai của tôi là - có phải vậy không? Nếu đúng thì tại sao? Và cách tốt nhất là gì?

Cách tốt nhất là chuỗi lời hứa:

function findFirst() { 
    return Entity.find(1).tap(function(result) { 
    if (!result) throw new Error("no result"); 
    }); 
} 

Đó là tốt hơn vì nó đơn giản hơn, ngắn hơn, dễ đọc hơn và ít đường dễ bị lỗi để làm chính xác những điều tương tự.

+0

Cảm ơn bạn đã trả lời ngắn. Tôi chỉ cần chú ý đến bài báo bạn đã đề cập (mã lỗi so với trường hợp ngoại lệ) - nếu bạn đọc kỹ, bạn sẽ thấy rằng các ngoại lệ có nhiều hạn chế và chúng "nên được tất cả các lập trình viên lười biếng viết mã chất lượng thấp chấp nhận như bản thân mình". Có lẽ nó chỉ là vấn đề của ý kiến. Cảm ơn vì lời đáp sâu sắc! –

+1

@AndreyPopov Tôi nghĩ bạn nên gỡ bỏ [khái niệm cơ bản] (http://codebetter.com/karlseguin/2006/04/05/understanding-and-using-exceptions/) trừ khi bạn làm việc tại NASA, trong trường hợp đó bạn nên sử dụng mã lỗi – Esailija

8

Xin hỏi chỉ là vấn đề duy nhất cho mỗi bài :-)

.then(success, fail) thực sự một mô hình chống và tôi nên luôn luôn sử dụng .then(success).catch(error)?

No.They just do different things, nhưng một khi bạn biết rằng bạn có thể chọn cái thích hợp.

Cách sử dụng promisify và lợi ích của nó là gì?

Tôi nghĩ rằng Bluebird Promisification docs giải thích điều này khá tốt - nó được sử dụng để convert a callback api để trả về lời hứa.

Nói về lời hứa/A +, cách thích hợp để từ chối lời hứa - là gì?

Có, việc ném lỗi hoàn toàn ổn. Bên trong số gọi lại then, bạn có thể ném và sẽ tự động bị phát hiện, dẫn đến việc từ chối lời hứa kết quả.

Bạn cũng có thể sử dụng return Promise.reject (new Error(…));; cả hai sẽ có tác dụng tương tự.

Một người bạn của tôi đã giúp tôi ra và tôi đã sử dụng một lời hứa mới để sửa chữa mọi thứ lên, như thế này: [...]

số You really shouldn't use that. Chỉ cần sử dụng then và ném hoặc trả lại lời hứa bị từ chối trong đó.

Nhưng nếu tôi bỏ lỡ tuyên bố bắt - toàn bộ ứng dụng của tôi sẽ phát nổ!

Không, nó sẽ không. Lưu ý rằng phương pháp .catch() không phải là tuyên bố try catch - lỗi sẽ bị phát hiện khi cuộc gọi lại then của bạn được gọi. Sau đó nó chỉ được chuyển đến cuộc gọi lại catch làm đối số, yêu cầu gọi số .catch() để nắm bắt ngoại lệ.

Và khi bạn bỏ lỡ .catch(), ứng dụng của bạn sẽ không bị thổi. Tất cả những gì xảy ra là bạn có một lời hứa bị từ chối nằm xung quanh, phần còn lại của ứng dụng của bạn sẽ không bị ảnh hưởng bởi điều này. Bạn sẽ nhận được unhandled rejection event, có thể là dealt with globally.

Tất nhiên, rằng nên không xảy ra; mọi lời hứa chuỗi (không mọi lời hứa dụ) phải được kết thúc bằng số .catch() xử lý lỗi một cách thích hợp. Nhưng bạn chắc chắn không cần .catch() trong mọi chức năng trợ giúp, khi nó trả về lời hứa ở một nơi khác thì lỗi sẽ thường được xử lý bởi người gọi.

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