Làm việc với Promises trong AngularJs

1, Giới thiệu

Javascript nói chung và trong AngularJs nói riêng, các process được xử lý một cách bất đồng bộ. Thực tế, có những lúc chúng ta cần các process được xử lý theo trình tự. Ví dụ chúng ta muốn thực hiện một ajax call để lấy user profile, sau khi ajax call thực hiện xong mới thực hiện việc lọc tách thông tin và show thông tin đó cho Admin. AngularJs có một build in service được gọi là $q giúp chúng ta thực hiện được điều đó.

2, Dùng $q service để tạo các promise

Một promise trong AngularJs có 2 loại object. Thứ nhất là promise object, nó được dùng để nhận các thông báo, các yêu cầu về những thứ cần xảy ra trong tương lai. Thứ hai là deferred object, nó được dùng để gửi các thông báo tới promise object.

Trong bài viết này, tôi sẽ tạo một application nhỏ để minh họa cho cách dùng $q service $q service có các methods sau dùng để quản lý các promise

all(promises) Trả về một promise đã được xử lý khi tất cả promises đầu vào được xử lý hoặc có bất kỳ promise đầu vào nào bị reject
defer() Tạo một deferred object
reject(reason) Trả về một promise mà bị reject
when(value) Đưa một value vào trong một promise đã resolve

2.1, Khởi tạo app view và app controller

a, View: promise.html.erb

<div class="row" ng-app="myApp" ng-controller="promiseController"> 
  <div class="col-md-8 col-md-offset-2"> 
    <div class="well"> 
      <button class="btn btn-primary">Heads</button> 
      <button class="btn btn-primary">Tails</button> 
      <button class="btn btn-primary">Abort</button> 
      Outcome: <span></span> 
    </div> 
  </div> 
</div>

b, Controller: promiseController.js

'use strict'; 

angular.module('myApp').controller('promiseController', promiseController); 
promiseController.$inject = ['$scope']; 

function promiseController($scope) { 
  console.log('ok') 
}

Giao diện ban đầu của app sẽ giống như sau:

2.2, Deferred object

Như đã nói ở trên, deferred object dùng để gửi notification tới promise object. Trong app của chúng ta deferred object được dùng để report sự kiện khi user click vào các button. Một deferred object có các method và property sau:

resolve(result) Gửi thông báo rằng deferred activity đã hoàn thành với một giá trị xác định
reject(reason) Gửi thông báo rằng deferred activity đã bị fail hoặc sẽ không được hoàn thành vì một lý do nào đó
notify(result) Cung cấp một kết quả tạm thời từ deferred activity
promise Trả về promise object mà nhận notification từ các method khác

Trong demo app tôi sẽ tạo một directive và dùng một deferred object. PromiseWorker.js

'use strict';

angular.module('myApp').directive('promiseWorker', promiseWorker);
function promiseWorker($q) {
  var deferred = $q.defer();
  return {
    link: function(scope, element, attrs) {
      element.find('button').on('click', function(event) {
        var buttonText = event.target.innerText;
        if(buttonText == 'Abort') {
          deferred.reject('Aborted');
        } else {
          deferred.resolve(buttonText);
        }
      });
    },
    controller: function($scope, $element, $attrs) {
      this.promise = deferred.promise;
    }
  };
}

Trong factory function của directive tôi có dùng $q.defer() để tạo ra một deferred object, deferred object này được dùng trong cả link function và controller. Link function dùng jqLite để bắt các event khi click vào các button, trong function xử lý sự kiện chúng ta lấy text của button vừa được click, từ text có được chúng ta sẽ quyết định xem sẽ resolve hay reject deferred đó. Trong controller function, chúng ta map promise property của nó tới promise property của deferred object. Với property này trong controller, chúng ta có thể cho phép các directive khác get lấy promise object này từ đó có được các notification về những gì sẽ xảy ra trong tương lai

2.3, Promise object

Phần trên, chúng ta đã định nghĩa một deferred object và dùng object này để tạo notification mỗi khi user thực hiện click vào button. Bước tiếp theo của chúng ta là đi tạo một directive để nhận các notification, thực hiện các task cụ thể thông qua việc xem xét promise object đã được tạo mà cụ thể ở đây là update nội dung của span element.

angular.module('myApp').directive('promiseObserver', promiseObserver);
function promiseObserver() {
  return {
    require: '^promiseWorker',
    link: function(scope, element, attrs, ctrl) {
      ctrl.promise.then(function(result) {
        element.text(result);
      }, function(reason) {
        element.text('Fail (' + reason + ' )');
      });
    }
  }
}

promiseObserver directive dùng require property để lấy controller từ promiseWorker directive và từ đó lấy được promise object đã tạo. Promise object có các method sau:

then(success, error, notify) Đăng ký các function được gọi tương ứng với các method resolve, reject, notify của deferred object
catch(error) Đăng ký error handle function tương ứng với reject method của deferred object
finally(fn) Đăng ký một function được gọi bất kể promise được resolve hay reject

2.4, Sử dụng các directive

Như vậy, chúng ta đã định nghĩa các directive promiseWorker, promiseObserver. Cuối cùng để kiểm chứng những gì chúng ta đã làm có hiệu quả như thế nào, chúng ta cần implement các directive đã tạo vào view. promise.html.erb:

<div class="row" ng-app="myApp" ng-controller="promiseController">
  <div class="col-md-8 col-md-offset-2">
    <div class="well" promise-worker>
      <button class="btn btn-primary">Heads</button>
      <button class="btn btn-primary">Tails</button>
      <button class="btn btn-primary">Abort</button>
      Outcome: <span promise-observer></span>
    </div>
  </div>
</div>

Kết quả: Khi click vào button Heads

3, Kết luận

Trong bài viết này, tôi đã giới thiệu tới các bạn lý thuyết cơ bản nhất về promise trong AngularJs: khái niệm, các loại object, các method, cũng như cách sử dụng promise thông qua một ví dụ cụ thể. Cảm ơn các bạn đã đọc bài viết, hẹn gặp lại trong các bài viết tiếp theo.

Tài liệu tham khảo

https://docs.angularjs.org/api/ng/service/$q http://andyshora.com/promises-angularjs-explained-as-cartoon.html http://thapsang.net/angularjs/promises-deferred-trong-angularjs.html http://www.dwmkerr.com/promises-in-angularjs-the-definitive-guide/


All Rights Reserved