Introduction to ES6 Promises – The Four Functions You Need To Avoid Callback Hell - part 1

Ngoài sự mới mẻ và sáng bóng, Promises là cách tuyệt vời để làm sạch code của bạn, giảm phụ thuộc vào các thư viện bên ngoài, và giúp bạn đồng bộ trong khi chờ đợi ES7. Người ta nói rằng, promises có thể mất nhiều công sức để hiểu, họ cảm thấy chúng hoàn toàn khác biệt so với các callbacks mà chúng ta thường sử dụng, và có một vài điều thú vị và bất ngờ trong thiết kế của nó khiến những người mới gặp bối rối.

So why should I learn promises, again?

Nếu những gì giới thiệu về Promises vẫn chưa đủ để thuyết phục bạn, hãy tìm hiểu ví dụ sau: Trước khi promises đến, các developer javascript đã sử dụng callbacks, setTimeout, XMLHttpRequest, về cơ bản tất cả các trình duyệt thực hiện các chức năng đồng bộ dựa trên callback. Để giải thích vấn đề với callback, chúng ta hãy thực hiện một số animation mà không có html và css:

  1. Chạy một vài dòng code
  2. Đợi 1s
  3. Chạy một vài dòng code khác
  4. Đợi 1s tiếp
  5. Lại thực hiện chạy một vài dòng code khác

Đây là một pattern mà chúng ta thường sử dụng với hình động CSS3. Nếu thực hiện sử dụng setTimeout, code của bạn sẽ như sau:

runAnimation(0);
setTimeout(function() {
    runAnimation(1);    
    setTimeout(function() {
        runAnimation(2);
    }, 1000);
}, 1000);

Trông thật kinh khủng 😄. Nhưng hãy tưởng tượng spec thực hiện 10 bước chứ không phải 3 - code sẽ gồm rất nhiều whitespace với mô hình kim tự tháp. Và mọi người hay gọi tên cho nó là: Callback Hell. Và những callback hell này xuất hiện ở mọi nơi: trong việc handle HTTP request, thao tác với database, animation, giao tiếp inter-process... Nhưng, chúng sẽ không xuất hiện trong code mà sử dụng Promises

But what are promises?

Promise được đưa vào Javascript từ ES6, đây có thể coi là một kỹ thuật nâng cao giúp xử lý vấn đề bất đồng bộ hiệu quả hơn. Trước đây kết quả của một tác vụ đồng bộ và bất đồng bộ sẽ trả về một kiểu dữ liệu nào đó hoặc thực hiện một Callback Function, điều này quá là bình thường bởi ta đã thấy kể từ ngày bắt đầu học về hàm trong lập trình phải không nào 😃. Với trường hợp thực hiện Callback Function thì sẽ dễ xảy ra lỗi Callback Hell, nghĩa là gọi callback quá nhiều và lồng nhau nên dẫn đến không kiểm soát được chương trình hoặc bộ nhớ không đủ để hoạt động. Và Trong bài này chúng ta sẽ tìm hiểu về Promise, một package được đưa vào từ ES6 giúp giải quyết vấn đề Callback Hell này.

Nếu so sánh với các phướng thức callback-based truyền thống thì promise cung cấp một biện pháp thay thế đơn giản hơn trong việc thực thi, biên soạn và quản lý các tác vụ bất đồng bộ. Hơn cả, promise cho phép chúng ta handle các lỗi xảy ra khi thực thi tác vụ bất đồng bằng các sử dụng các phương thức tiếp cận tương tự với sử dụng try/catch trong các tác vụ đồng bộ (synchronous) thông thường.

Promise có thể ở một trong ba trạng thái.

  • Pending – trang thái khởi tạo, lúc này kết quả của promise vẫn chưa được xác định, bởi vì tác vụ bất đồng bộ vẫn chưa hoàn thành.
  • Fulfilled – trạng thái hoàn thành, tác vụ bất đồng bộ đã hoàn thành, và promise đã có giá trị.
  • Rejected – trạng thái bị từ chối, tác vụ bất đồng bộ thực thi thất bại, và promise sẽ không bao giờ được hoàn thành (fulfilled). Trong trạng thái bị từ chối (rejected) một promise sẽ chứa một lý do (reason) để chỉ ra tại sao việc thực thi thất bại.

Khi một promise đang ở trạng thái khởi tạo (pending), nó có thể chuyển sang trạng thái hoàn thành (fulfilled) hoặc bị từ chối (rejected). Một khi promise được hoàn thành hoặc bị từ chối, thì đó sẽ là giá trị cuối cùng, promise đó sẽ không thể chuyển thành trạng thái khác được nữa, và vì thế giá trị của nó hoặc lý do thực hiện thất bại sẽ không thay đổi.

Hãy xem sự khác biệt của promises với callbacks. Dưới đây là 4 khác biệt chính:

1. Callbacks are functions, promises are objects

Callback chỉ là các khối block có thể chạy để response các sự kiện như khi bộ đếm giờ tắt hoặc các tin nhắn nhận được từ máy chủ. Bất kì một function có thể là một callback, và mỗi một callback là một function. Promises là những đối tượng, lưu trữ thông tin về những sự kiện, sự kiện đó có xảy ra hay không, nếu có thì trả về những kết quả gì

2. Callbacks are passed as arguments, promises are returned

Callbacks được định nghĩa một cách độc lập với các function gọi tới chúng - chúng được truyền như là những arguments. Những function sau đó lưu trữ callback, và gọi chúng khi event thực sự xảy ra. Promises được tạo ra bên trong các function không đồng bộ, và sau đó được return. Khi một event xảy ra, các function không đồng bộ tiến hành update promise để thông báo cho thế giới bên ngoài.

3. Callbacks handle success and failure, promises don’t handle anything

Callback thường được gọi với những thông tin về một hành động nào đó thành công hay thất bại, và phải có khả năng xử lý cả hai kịch bản Promises không xử lý bất cứ điều gì theo mặc định, nhưng handle thành công hay thất bại sẽ được attack vào sau.

4. Callbacks can represent multiple events, promises represent at most one

Callbacks có thể được gọi nhiều lần bởi những function mà chúng được truyền đến Promises chỉ đại diện cho một event duy nhất, hoặc thành công, hoặc thất bại

Chúng ta hãy cùng đi vào chi tiết

The four functions you need to know

1. new Promise(fn)

Promises được khởi tạo bằng lời gọi new Promise với argument là một function(). Ví dụ:

    // Creates a Promise instance which doesn't do anything.
    // Don't worry, I'll explain this in more detail in a moment.
    promise = new Promise(function() {});

Lời gọi new Promise sẽ ngay tức khắc gọi function, cái mà đã được truyền vào như là một argument. Mục đích của fuction này là để thông báo cho đối tượng Promise khi sự kiện, cái mà promise đại diện được resolved hay rejected. Để xử lý được như vậy, function mà bạn truyền vào cần có 2 argument: resolve và reject. Gọi resolve(value) khi handle ra kết quả thành công, còn reject(value) khi handle thất bại. Bạn không nên gọi cả 2.

Hãy ứng dụng promise vào ứng dụng bên trên. Ví dụ trên đã sử dụng function setTimeout, nhằm thực hiện một callback, giờ ta muốn dùng promise thay thế:

    // Return a promise which resolves after the specified interval
    function delay(interval) {
        return new Promise(function(resolve) {
            setTimeout(resolve, interval);
        });
    }

    var oneSecondDelay = delay(1000);

Ta đã có một promise, cái mà sẽ xử lý sau 1s được truyền đến. Chúng ta có thể thực hiện các hành động sau 1s đó với promise.then Function mà chúng ta truyền trong lời gọi new Promise bên trên mới chỉ chứa argument resolve, vẫn còn thiếu reject, đó là bởi vì setTimeout không bao giờ false, do đó không có kịch bản nào cho trường hợp reject.

Nếu chúng ta sử dụng setTimeout cho các animation và animation sẽ fail do trình duyệt không suppost:

    function animationTimeout(step, interval) {
        new Promise(function(resolve, reject) {
            if (isAnimationSupported(step)) {
                setTimeout(resolve, interval);
            } else {
                reject('animation not supported');
            }
        });
    }

    var firstKeyframe = animationTimeout(1, 1000);

Vậy Promise là một gói dùng để quản lý kết quả trả về của một hành động Asycn (bất đồng bộ) và nó vừa được bổ sung vào ngôn ngữ Javascript từ version ES6. Việc nắm vững Promise không hề đơn giản và không phải ai cũng hiểu rõ, vì vậy với bài viết này sẽ giúp bạn có cái nhìn tổng quan về Promise trong Javascript nói chung và trong ES6 nói riêng. Bài viết sẽ được tiếp tục trong phần 2

Nguồn: http://jamesknelson.com/grokking-es6-promises-the-four-functions-you-need-to-avoid-callback-hell/