JavaScript非同步程式設計助手:Promise模式

csdn發表於2013-08-14

  非同步模式在Web程式設計中變得越來越重要,對於Web主流語言JavaScript來說,這種模式實現起來不是很利索,為此,許多JavaScript庫(比如 jQuery和Dojo、AngularJS)新增了一種稱為Promise的抽象(術語稱作Deferred模式)。通過這些庫,開發人員能夠在實際程式設計中使用Promise模式,每個Promise都擁有一個叫做then的唯一介面,當Promise失敗或成功時,它就會進行回撥。它代表了一種可能會長時間執行而且不一定必須完成的操作結果。這種模式不會阻塞和等待長時間的操作完成,而是返回一個代表了承諾的(promised)結果的物件。

  本文我們將討論JavaScript庫(比如jQueryAngularJS)是如何使用Promise模式的來處理非同步的,其實就是通過回撥的方式提供容錯支援。

  下面讓我們來看看jQuery是如何操作的:

var $info = $("#info");
$.ajax({
    url:"/echo/json/",
    data: { json: JSON.stringify({"name": "someValue"}) },
    type:"POST",
    success: function(response)
    {
       $info.text(response.name);
    }
});

  在這個例子中,你可以看到當設定成功後會指定一個回撥,這並不是Promise,但卻是一種很好的回撥方式。當Ajax呼叫完成後,它便會執行success函式。根據庫所使用的非同步操作,你可以使用各種不同的回撥(即任務是否成功,都會進行回撥,做出響應)。使用Promise模式會簡化這個過程,非同步操作只需返回一個物件呼叫。這個Promise允許你呼叫一個叫做then的方法,然後讓你指定回撥的function(s)個數,下面讓我們來看看jQuery是如何建立Promise的:

var $info = $("#info");
$.ajax({
    url: "/echo/json/",
    data: {
        json: JSON.stringify({
            "name": "someValue"
        })
    },
    type: "POST"
})
.then(function (response) {
    $info.text(response.name);
});

  有趣的是,ajax物件返回xhr物件實現Promise模式,所以我們可以呼叫then方法,這樣做的優勢是你可以鏈式呼叫,實現獨立操作,如下所示 :

var $info = $("#info");
$.ajax({
    url: "/echo/json/",
    data: {
        json: JSON.stringify({
            "name": "someValue"
        })
    },
    type: "POST"
})
.then(function (response) {
    $info.text(response.name);
})
.then(function () {
    $info.append("...More");
})
.done(function () {
    $info.append("...finally!");
});

  由於許多庫都開始採用Promise模式,所以非同步操作會變的非常容易。但如果站在相反的角度思考,Promise將會是什麼樣子的呢?其中一個非常重要的模式是函式可以接受兩種功能,一個是成功時的回撥,另一個是失敗時的回撥。

var $info = $("#info");
$.ajax({
    // Change URL to see error happen
    url: "/echo/json/",
    data: {
        json: JSON.stringify({
            "name": "someValue"
        })
    },
    type: "POST"
})
.then(function (response) {
    // success
    $info.text(response.name);
}, 
function () {
    // failure
    $info.text("bad things happen to good developers");
})
.always(function () {
    $info.append("...finally");
});

  需要注意的是,在jQuery裡,無論成功還是失敗,我們都會使用一個呼叫來指定我們想要呼叫的。下面讓來看看AngularJS是如何使用Promise模式的:

var m = angular.module("myApp", []);
m.factory("dataService", function ($q) {
    function _callMe() {
        var d = $q.defer();
        setTimeout(function () {
            d.resolve();
            //defer.reject();
        }, 100);
        return d.promise;
    }
    return {
        callMe: _callMe
    };
});
function myCtrl($scope, dataService) {
    $scope.name = "None";
    $scope.isBusy = true;
    dataService.callMe()
      .then(function () {
        // Successful
        $scope.name = "success";
      }, 
      function () {
        // failure
        $scope.name = "failure";
      })
      .then(function () {
        // Like a Finally Clause
        $scope.isBusy = false;
      });
}

  你可以在JSFiddle裡試試這些例子,並且看看會產生哪些效果。使用Promise來操作非同步是一種非常簡單的方式,而且還可以簡化你的程式碼,豈不是一舉兩得的好方法。

  更多關於Promise的介紹及示例,可以前往官網檢視

  英文來源:JavaScript Promise

相關文章