Promise實現多個異步流程控制

我覬覦Promise很久了。但因爲緣分未到,以及智商不夠,一直沒有機會在實戰中用它。前段時間的 2015 上海前端技術峯會 中的主題分享之《ES6實戰》讓我有這樣的感受:ES5的好多特性都尚未徹底掌握呢,ES6就來了。而後聽說明年ES7就要來了……前端

因此說,ajax

學無止境。數據庫

promise

背景描述
在瀏覽器端,有個表單,爲了填寫方便,兩個輸入框使用<select>元素供用戶選擇;此外,在編輯一個條目時(對應數據庫中一行),還要將該條目的數據從服務器獲取到,而後生成到表單中。瀏覽器

<form id="student-info">
    <input type="text" data-key="name">
    <select data-key="province">
        <option value="10">省份1</option>
        <option value="11">省份2</option>
    </select>
    <select data-key="university">
        <option value="1">學校1</option>
        <option value="2">學校2</option>
    </select>
</form>

幾個工具
沒有使用任何MVC或者MVVM框架。只是藉助於jQuery、Underscore等進行構建。服務器

從表單中獲取數據:框架

/*
 * selector: 表單容器的選擇器
 */
util.getDataFromForm = function(selector){
    var data = {};
    var inputs = $(selector).find(*[data-key]);

    inputs.each(function(index, element){
        var value = $(element).val();
        var key = $(element).data('key');

        data[key] = value;
    });

    return data;
};

把數據渲染到表單中:異步

/**
 * data: 模型數據
 * selector: 表單容器的選擇器
 */
util.renderFormWithData = function(data, selector){
    var inputs = $(selector).find(*[data-key]);

    inputs.each(function(index, element){
        var key = $(element).data('key');
        $(element).val(data[key]);
    });
};

最初我是這樣寫Promise代碼的
OK,對於上面這個表單,兩個select元素是須要從服務器GET數據後生成的(藉助於模板,在此不放代碼了)。而後若是要編輯一個條目,還須要額外GET該條目的數據並賦值到表單裏。函數

若是不使用Promise,而是隻用回調寫法,那麼最多的狀況下,須要嵌套三層。任何一層失敗了,都很差辦。而藉助於Promise,能夠大膽地像同步代碼那樣寫:工具

版本-0.0.1
Promise異步流程控制-0.0.1

function prepareForm(callback){
    var promise = new Promise(function(resolve, reject) {
        resolve();
    });

    promise.then(function(){
        $.ajax({
            url: '/rest/province/all',
            type: 'get',
            success: function(){
                /* render the province select element */
            }
        });
    })
    .then(function(){
        $.ajax({
            url: '/rest/university/all',
            type: 'get',
            success: function(){
                /* render the university select element */
            }
        });
    })
    .then(function(){
        if(callback){
            callback();
        }
    })
    .catch(function(error){
        console.log('error: ', error);
    });
};

在這裏,prepareForm函數依然接受一個回調函數做爲參數。若是是ADD操做,則回調中無須再次執行一個Ajax操做;若是是UPDATE操做,則回調函數須要對該條目進行Ajax請求,而後根據這個值去渲染表單——這時不需擔憂表單裏的兩個select元素還沒有就緒,由於必定是就緒了以後才根據模型對象渲染表單的。

注意:每次調用then都會返回一個新建立的Promise對象。

版本-0.0.2
版本-0.0.1並無很好地處理rejected狀況。
【未完待續】

後來我意識到Promise應該這樣用

上面我誤覺得直接鏈式then()方法就是Promise處理異步流程的精髓了。然而大錯特錯。對於異步任務,重要的是保證數據的獲取。這一般就須要自定義一個殼,來包裝一下Ajax請求的參數、返回數據等,尤爲要把返回的數據傳給resolve(),以便在then()裏可使用這個數據。下面是例子。

/* 先定義一個返回Promise對象的Ajax過程 */
var getAjaxPromise = function(option){
    return new Promise(function(resolve, reject){
        $.ajax({
            url: option.url,
            type: 'get',
            data: option.data || '',
            success: function(data){
                resolve(data);
            },
            error: function(error){
                reject(error);
            }
        });
    });
};
 
/* 啓動第1個異步任務 */
var p1 = getAjaxPromise({
    url: 'root/url/1'
});
 
p1.then(function(data1){
    /* 處理第1個異步任務的結果 */
    console.log(data1);

    /* 而後啓動第2個異步任務 */
    return getAjaxPromise({
        url: 'root/url/2'
    });
})
.then(function(data2){
    /* 處理第2個異步任務的結果 */
    console.log(data2);
});

then() 方法能夠鏈式調用,關鍵就是每一個then都會返回一個新的Promise對象。

相關文章
相關標籤/搜索