1. 程式人生 > >JavaScript Promise 告別非同步亂巢狀

JavaScript Promise 告別非同步亂巢狀

在JavaScript語言中,無論是寫瀏覽器端的各種事件處理回撥、ajax回撥,還是寫Node.js上的業務邏輯,不得不面對的問題就是各種回撥函式。回撥函式少了還好,一旦多了起來而且必須講究執行順序的話,回撥函式開始巢狀,那程式碼的噁心程度是相當不符合常人的線性思維的。

說到這裡,我真有點想去了解一下響馬老師的fibjs,我沒有接觸過它,但它的程式設計方式就是線性的,更加符合常人思維。我個人認為Promise就是為了把JS複雜的巢狀轉換成常人思維的線性程式碼。

// 就像下面這樣:
// 你不在乎下面這三個ajax的執行順序還好
// 如果你在乎順序呢?
$.get('url', function()
{ }, 'json'); $.get('url1', function(){ }, 'json'); $.get('url2', function(){ }, 'json'); // 就像這樣? $.get('url', function(){ $.get('url1', function(){ $.get('url2', function(){ }, 'json'); }, 'json'); }, 'json'); // 下面是我最近寫的一段Node.js的程式碼 // 其實這個巢狀也不算多 // 如果業務邏輯相當複雜起來呢? // 巢狀20 30層?
var adminIndex = function(params, callback){ storeAdmin.getApiTokens(function(err, tokens){ if ( err ) { callback(err); return; } storeAdmin.getApiServices(function(err, apiServices){ if ( err ) { callback(err); return; } storeAdmin.getSocketioServices(function(err, socketioServices)
{ if ( err ) { callback(err); return; } callback(0, { status : true, data : { api_tokens : tokens, api_services : apiServices, socketio_services : socketioServices } }); }); }); }); };

說了這麼多,到底什麼是Promise呢?

其實,Promise就是一個類,而且這個類已經成為了ES6的標準,這個類目前在chrome32、Opera19、Firefox29以上的版本都已經支援了,要想在所有瀏覽器上都用上的話就看看es6-promise吧。

那Promise怎麼用呢?

看一段很簡單的程式碼,請注意閱讀程式碼中的註釋。

var val = 1;

// 我們假設step1, step2, step3都是ajax呼叫後端或者是
// 在Node.js上查詢資料庫的非同步操作
// 每個步驟都有對應的失敗和成功處理回撥
// 需求是這樣,step1、step2、step3必須按順序執行
function step1(resolve, reject) {
    console.log('步驟一:執行');
    if (val >= 1) {
        resolve('Hello I am No.1');
    } else if (val === 0) {
        reject(val);
    }
}

function step2(resolve, reject) {
    console.log('步驟二:執行');
    if (val === 1) {
        resolve('Hello I am No.2');
    } else if (val === 0) {
        reject(val);
    }
}

function step3(resolve, reject) {
    console.log('步驟三:執行');
    if (val === 1) {
        resolve('Hello I am No.3');
    } else if (val === 0) {
        reject(val);
    }
}

new Promise(step1).then(function(val){
    console.info(val);
    return new Promise(step2);
}).then(function(val){
    console.info(val);
    return new Promise(step3);
}).then(function(val){
    console.info(val);
    return val;
}).then(function(val){
    console.info(val);
    return val;
});

// 執行之後將會列印
步驟一:執行
Hello I am No.1
步驟二:執行
Hello I am No.2
步驟三:執行
Hello I am No.3
Hello I am No.3

Promise到底解決什麼問題?

正如上面程式碼所示,筆者認為,Promise的意義就在於 ** then 鏈式呼叫** ,它避免了非同步函式之間的層層巢狀,將原來非同步函式的 巢狀關係 轉變為便於閱讀和理解的 鏈式步驟關係 。

Promise的主要用法就是將各個非同步操作封裝成好多Promise,而一個Promise只處理一個非同步邏輯。最後將各個Promise用鏈式呼叫寫法串聯,在這樣處理下,如果非同步邏輯之間前後關係很重的話,你也不需要層層巢狀,只需要把每個非同步邏輯封裝成Promise鏈式呼叫就可以了。

Promise常用的關鍵點

在Promise定義時,函式已經執行了

Promise建構函式只接受一個引數,即帶有非同步邏輯的函式。這個函式在 new Promise 時已經執行了。只不過在沒有呼叫then 之前不會 resolve 或 reject。

在then中的resolve方法中如何return?

在then方法中通常傳遞兩個引數,一個 resolve 函式,一個 reject 函式。reject暫時不討論,就是出錯的時候執行的函式罷了。resolve 函式必須返回一個值才能把鏈式呼叫進行下去,而且這個值返回什麼是有很大講究的。

  • resolve 返回一個新 Promise

返回一個新Promise之後再呼叫的then就是新Promise中的邏輯了。

  • resolve 返回一個值

返回一個值會傳遞到下一個then的resolve方法引數中。