Promise

Promise là một đối tượng đặc biệt dùng cho các xử lý bất đồng bộ. Nó đại diện cho một xử lý bất đồng bộ và chứa kết quả cũng như các lỗi xảy ra từ xử lý bất đồng bộ đó.

Cú phápPhần

new Promise(executor);
new Promise(function(resolve, reject) { ... } );

Tham số đầu vàoPhần

executor
Một hàm có 2 tham số đầu vào là 2 hàm phản hồi resolve và reject. Hàm resolve sẽ được gọi khi xử lý thành công, còn reject sẽ được gọi khi xử lý thất bại. 
* Chú ý: 2 hàm phản hồi này rất dễ bị nhầm lẫn với phong cách của hàm phản hồi của Node.js. Với Node.js hàm phản hồi lỗi thường là tham số đầu tiên, còn Promise thì ngược lại.
Hàm executor sẽ được gọi ngay khi Promise được gọi tới, tức là nó còn được chạy trước cả hàm khởi tạo trả ra kết quả của Promise. Sau khi xử lý kết thúc tùy theo tình huống mà hàm phản hồi resolve hoặc reject sẽ được gọi tới. Trường hợp xử lý thành công thì hàm resolve sẽ được gọi tới để trả ra kết quả. Còn trường hợp thất bại thì hàm reject sẽ được gọi tới để trả ra mã lỗi thực thi.

Mô tảPhần

Một Promise có thể như một proxy đại diện cho một giá trị mà ta không cần phải biết ngay khi khởi tạo. Bằng các sử dụng Promise  ta có thể kết hợp với các hàm xử lý khác để sử dụng kết quả sau khi thực thi xử lý bất đồng bộ mà nó đang đại diện. Vì vậy mà ta có thể lập trình bất đồng bộ gần giống với kiểu lập trình đồng bộ - tức là đợi xử lý bất đồng bộ xong mới thực thi các thao tác mà cần sử dụng tới kết quả của xử lý đó. Để có thể làm được việc đó thay vì trả ra kết quả của việc xử lý đồng bộ, Promise  sẽ trả ra một promise khác. Bằng promise mới này ta lại có thể lặp lại việc sử dụng kết quả của thao tác xử lý lúc trước để làm đầu vào cho các thao tác xử lý lúc sau.
Tại mỗi thời điểm Promise sẽ có thể ở một trong các trạng thái sau:
  • pending: Trạng thái chờ xử lý kết thúc. Trạng thái này chính là trạng thái ban đầu của Promise, nó thể hiện rằng thao tác xử lý của ta chưa kết thúc.
  • fulfilled: Trạng thái xử lý thành công. Trạng thái này thể hiện rằng thao tác xử lý của ta đã kết thúc và thành công.
  • rejected: Trạng thái xử lý thất bại. Trạng thái này thể hiện thao tác xử lý đã kết thúc và thất bại.
Như vậy một Promise khi ở trạng thái pending sẽ được chuyển thành trạng thái fulfilled với kết quả thành công hoặc trạng thái rejected kèm với mã lỗi xảy ra khi xử lý kết thúc. Sau khi xử lý kết thúc, bất kể trạng thái được chuyển thành là thành công hay thất bại thì các hàm xử lý được đính kèm sẽ được gọi thực thi. Để đính kèm một hàm cho Promise, ta có thể sử dụng Promise.prototype.then() cho trường hợp thành công và Promise.prototype.catch() cho trường hợp xử lý thất bại.
Hàm đính kèm xử lý Promise.prototype.then() và Promise.prototype.catch() sẽ trả ra một promise khác nên thao tác hậu xử lý bằng hàm đính kèm có thể được chuyển tiếp kiểu xử lý chuỗi (chained). Cụ thể hơn ta có thể xem hình dưới đây.
Như hình minh họa hoạt động của Promise trên, ta có thể thấy khi khởi tạo Promise sẽ có trạng thái là pending. Sau khi xử lý kết thúc, tùy theo kết quả xử lý mà trạng thái sẽ là fullfil hoặc reject. Lúc đó các hàm đính kèm sẽ được thực thi thông qua hàm Promise.prototype.then() hoặc Promise.prototype.catch(). Chính các hàm này lại trả ra một Promise khác nên ta có thể xử lý một loạt các thao tác phía sau một cách chuyển tiếp.

Đừng nhầm lẫn với: một số ngôn ngữ khác như Scheme cũng có khái niệm “promises” - nhưng khái niệm này để chỉ thị một thao tác được gọi thực thi sau. Còn, Promises trong JavaScript biểu diễn các thao tác bất đồng bộ mà đã được thực thi (thao tác bất đồng bộ này được gọi ngay khi ta gọi Promise - ngay cả trước khi hàm khởi tạo của Promise được gọi tới) và có thể đính kèm các hàm hậu xử lý sau khi xử lý bất đồng bộ mà nó biểu diễn kết thúc. Nếu bạn muốn dùng các thao tác kiểu thi sau như vậy thì có thể sử dụng hàm mũi tên (arrow function) không có tham số đầu vào, như: f = () => expression để tạo một hàm được gọi sau, và sử dụng f() để thực thi nó.
Lưu ý: Promise được gọi kết thúc (settled) khi và chỉ khi nó ở trạng thái fulfilled (thành công) hoặc rejected (thất bại). Đôi lúc có thể bạn thấy đâu đó nói rằng Promise được giải quyết xong (resolved) để ám chỉ rằng Promise được kết thúc, lúc đó đừng nhầm lẫn là nó được kết thúc thành công vì nó chỉ đơn giản là nói tới Promise đã được kết thúc mà thôi. Để biết rõ hơn về các thuật ngữ liên quan tới Promise, bạn có thể tham khảo bài viết này: States and fates.

Thuộc tínhPhần

Promise.length
Thuộc tính length này luôn có giá trị là 1 (số lượng của tham số khởi tạo).
Promise.prototype
Biểu diễn prototype cho hàm khởi tạo Promise.

Phương thứcPhần

Promise.all(iterable)
Phương thức này được sử dụng khi ta cần đợi một tập các Promise kết thúc. Trả ra một promise đại diện cho tất cả các kết quả thu được từ các promise nằm trong iterable sau khi tất cả các promise này kết thúc xử lý thành công. Hoặc, trả ra một promise đại diện cho lỗi thực thi ngay khi một promise nào đó kết thúc lỗi, khi đó promise được trả ra cũng sẽ ở trạng thái lỗi. Khi tất cả các promise trong iterable kết thúc thành công thì promise trả ra cũng ở trạng thái thành công với kết quả là một mảng chứa tất cả các kết quả của các promise đã thực thi theo đúng thứ tự trong iterable. Còn khi một promise nào đó xảy ra lỗi thì promise được trả ra cũng sẽ ở trạng thái lỗi và chứa mã lỗi của promise đầu tiên gây lỗi đó.
Promise.race(iterable)
Trả ra một promise ngay sau khi một trong các promise trong iterable kết thúc xử lý. Tức là dù kết quả thu được là lỗi hay thành công thì ta cũng sẽ trả ngay ra một promise mới và promise mới này sẽ chứa kết quả của promise được kết thúc đầu tiên.
Promise.reject(reason)
Trả ra một promise trạng thái lỗi với mã lỗi mà hàm xử lý trả ra. Hàm này sẽ được gọi tới khi hàm xử lý của ta bị lỗi (thất bại).
Promise.resolve(value)
Trả ra một promise thành công với kết quả mà hàm xử lý trả ra. 

Nguyên mẫu PromisePhần

Thuộc tínhPhần

Promise.prototype.constructor
Returns the function that created an instance's prototype. This is the Promise function by default.

Phương thứcPhần

Promise.prototype.catch(onRejected)
Appends a rejection handler callback to the promise, and returns a new promise resolving to the return value of the callback if it is called, or to its original fulfillment value if the promise is instead fulfilled.
Promise.prototype.then(onFulfilled, onRejected)
Appends fulfillment and rejection handlers to the promise, and returns a new promise resolving to the return value of the called handler, or to its original settled value if the promise was not handled (i.e. if the relevant handler onFulfilled or onRejected is not a function).
Promise.prototype.finally(onFinally)
Appends a handler to the promise, and returns a new promise which is resolved when the original promise is resolved. The handler is called when the promise is settled, whether fulfilled or rejected.

Ví dụPhần

Tạo một PromisePhần

Ví dụ nhỏ này sẽ mô tả cơ chế của một Promise. Hàm testPromise() sẽ được gọi tới mỗi khi ta click vào <button>. Ta sẽ sử dụng window.setTimeout() để thiết lập giá trị kết thúc cho nó. Hàm xử lý này sẽ đếm (bắt đầu từ 1) sau mỗi khoảng thời gian ngẫu nhiên từ 1 tới 3 giây.
Hàm hậu xử lý đính kèm ở đây chỉ đơn giản là một hàm log lại các giá trị được trả ra và được gán bằng cách sử dụng hàm p1.then().
'use strict';
var promiseCount = 0;

function testPromise() {
    var thisPromiseCount = ++promiseCount;

    var log = document.getElementById('log');
    log.insertAdjacentHTML('beforeend', thisPromiseCount +
        ') Started (<small>Sync code started</small>)<br/>');

    // Tạo một Promise: we promise a numeric count of this promise, starting from 1 (after waiting 3s)
    var p1 = new Promise(
        // The resolver function is called with the ability to resolve or
        // reject the promise
        function(resolve, reject) {
            log.insertAdjacentHTML('beforeend', thisPromiseCount +
                ') Promise started (<small>Async code started</small>)<br/>');
            // This is only an example to create asynchronism
            window.setTimeout(
                function() {
                    // We fulfill the promise !
                    resolve(thisPromiseCount);
                }, Math.random() * 2000 + 1000);
        }
    );

    // We define what to do when the promise is resolved/fulfilled with the then() call,
    // and the catch() method defines what to do if the promise is rejected.
    p1.then(
        // Log the fulfillment value
        function(val) {
            log.insertAdjacentHTML('beforeend', val +
                ') Promise fulfilled (<small>Async code terminated</small>)<br/>');
        })
    .catch(
        // Log the rejection reason
        function(reason) {
            console.log('Handle rejected promise ('+reason+') here.');
        });

    log.insertAdjacentHTML('beforeend', thisPromiseCount +
        ') Promise made (<small>Sync code terminated</small>)<br/>');
}
Ví dụ này được thực thi mỗi khi ta click vào button và để chạy được ví dụ này, bạn cần một trình duyệt có hỗ trợ Promise. Bạn hãy thử click vào button một vài lần liên tiếp trong một khoảng thời gian ngắn để thấy được các promise được xử lý thành chuỗi và sau khi kết thúc xử lý sẽ ở trạng thái thế nào nhé.

Ví dụ với XMLHttpRequestPhần

Tạo một PromisePhần

Ví dụ này sẽ trình bày một cách sử dụng Promise để lấy kết quả (thành công hoặc lỗi) trả về từ XMLHttpRequest.
'use strict';

// A-> $http function is implemented in order to follow the standard Adapter pattern
function $http(url){
 
  // A small example of object
  var core = {

    // Method that performs the ajax request
    ajax: function (method, url, args) {

      // Creating a promise
      var promise = new Promise( function (resolve, reject) {

        // Instantiates the XMLHttpRequest
        var client = new XMLHttpRequest();
        var uri = url;

        if (args && (method === 'POST' || method === 'PUT')) {
          uri += '?';
          var argcount = 0;
          for (var key in args) {
            if (args.hasOwnProperty(key)) {
              if (argcount++) {
                uri += '&';
              }
              uri += encodeURIComponent(key) + '=' + encodeURIComponent(args[key]);
            }
          }
        }

        client.open(method, uri);
        client.send();

        client.onload = function () {
          if (this.status >= 200 && this.status < 300) {
            // Performs the function "resolve" when this.status is equal to 2xx
            resolve(this.response);
          } else {
            // Performs the function "reject" when this.status is different than 2xx
            reject(this.statusText);
          }
        };
        client.onerror = function () {
          reject(this.statusText);
        };
      });

      // Return the promise
      return promise;
    }
  };

  // Adapter pattern
  return {
    'get': function(args) {
      return core.ajax('GET', url, args);
    },
    'post': function(args) {
      return core.ajax('POST', url, args);
    },
    'put': function(args) {
      return core.ajax('PUT', url, args);
    },
    'delete': function(args) {
      return core.ajax('DELETE', url, args);
    }
  };
};
// End A

// B-> Here you define its functions and its payload
var mdnAPI = 'https://developer.mozilla.org/en-US/search.json';
var payload = {
  'topic' : 'js',
  'q'     : 'Promise'
};

var callback = {
  success: function(data) {
    console.log(1, 'success', JSON.parse(data));
  },
  error: function(data) {
    console.log(2, 'error', JSON.parse(data));
  }
};
// End B

// Executes the method call 
$http(mdnAPI) 
  .get(payload) 
  .then(callback.success) 
  .catch(callback.error);

// Executes the method call but an alternative way (1) to handle Promise Reject case 
$http(mdnAPI) 
  .get(payload) 
  .then(callback.success, callback.error);

// Executes the method call but an alternative way (2) to handle Promise Reject case 
$http(mdnAPI) 
  .get(payload) 
  .then(callback.success)
  .then(undefined, callback.error);

Tải một ảnh với XHRPhần

Một ví dụ đơn giản khác được sử dụng Promise và XMLHttpRequest để tải về một ảnh là MDN  GitHub - promise-test. Ngoài ra, bạn có thể xem nó hoạt động ra sao tại đây. Mỗi bước đều được chú thích đầy đủ để giúp bạn hình dung được việc sử dụng Promise với XHR dễ dàng hơn.

Mô tảPhần

Mô tảTrạng tháiChú thích
ECMAScript 2015 (6th Edition, ECMA-262)
The definition of 'Promise' in that specification.
StandardInitial definition in an ECMA standard.
ECMAScript Latest Draft (ECMA-262)
The definition of 'Promise' in that specification.
Draft 

Trình duyệt hỗ trợPhần

No compatibility data found. Please contribute data for "" (depth: 1) to the MDN compatibility data repository.

Tham khảo thêm

Comments

Popular Posts