2016-08-09 104 views
-1

我有很多異步方法來執行,我的程序流可以根據每個方法的返回而改變很多。下面的邏輯就是一個例子。我無法使用Promise以易於閱讀的方式編寫它。你會如何寫它?避免javascript回調並承諾地獄

Ps:更復雜的流動是受歡迎的。

Ps2:is_business是一個預先定義的標誌,我們可以說我們是在寫「業務用戶」還是「人員用戶」。

begin transaction 
update users 
if updated 
    if is_business 
     update_business 
     if not updated 
      insert business 
     end if 
    else 
     delete business 
    end if 
else 
    if upsert 
     insert user 
     if is_business 
      insert business 
     end if 
    end if 
end if 
commit transaction 
+0

我認爲這是一個公平的問題。所有這些操作都被認爲是異步的嗎? – mooiamaduck

+0

是的@mooiamaduck。其實我正在做數據庫插入/更新都是異步的。 – Eduardo

+0

你有沒有看過'koa.js'(或'co',如果你沒有寫網絡應用程序)?如果你使用koa和承諾,它會看起來非常像你在那裏。或者,如果你想使用直接回調,你可以使用像async這樣的流量控制庫(例如,查看'async.auto'函數 – Kevin

回答

0

的解決方案是@mooiamaduck答案,@Kevin評論的混合。

使用承諾,ES6生成器和co庫使代碼更清晰。在閱讀postgresql節點庫示例(pg)時,我發現了一個很好的示例。在下面的例子中,pool.connectclient.query是返回Promises的異步操作。在創建result後,我們可以輕鬆添加if/else,然後進行更多的異步操作,使代碼保持同步。

co(function *() { 
    var client = yield pool.connect() 
    try { 
     yield client.query('BEGIN') 
     var result = yield client.query('SELECT $1::text as name', ['foo']) 
     yield client.query('INSERT INTO something(name) VALUES($1)', [result.rows[0].name]) 
     yield client.query('COMMIT') 
     client.release() 
    } catch(e) { 
    // pass truthy value to release to destroy the client 
    // instead of returning it to the pool 
    // the pool will create a new client next time 
    // this will also roll back the transaction within postgres 
    client.release(true) 
    } 
}) 
4

承諾的好處是它們在同步代碼和異步代碼之間做了一個簡單的比喻。爲了說明(使用Q庫):

同步:

var thisReturnsAValue = function() { 
    var result = mySynchronousFunction(); 
    if(result) { 
    return getOneValue(); 
    } else { 
    return getAnotherValue(); 
    } 
}; 

try { 
    var value = thisReturnsAValue(); 
    console.log(value); 
} catch(err) { 
    console.error(err); 
} 

異步:

var Q = require('q'); 

var thisReturnsAPromiseForAValue = function() { 
    return Q.Promise(function() { 
    return myAsynchronousFunction().then(function(result) { 
     if(result) { 
     // Even getOneValue() would work here, because a non-promise 
     // value is automatically cast to a pre-resolved promise 
     return getOneValueAsynchronously(); 
     } else { 
     return getAnotherValueAsynchronously(); 
     } 
    }); 
    }); 
}; 

thisReturnsAPromiseForAValue().then(function(value) { 
    console.log(value); 
}, function(err) { 
    console.error(err); 
}); 

你只需要就習慣了,返回值總是作爲參數訪問到隨後的想法並且鏈接承諾等同於組成函數調用(f(g(h(x))))或按順序執行函數(var x2 = h(x); var x3 = g(x2);)。這就是本質!當你引入分支時,事情會變得有點棘手,但你可以從這些基本原則中找出應該做的事情。因爲then-callbacks接受promise作爲返回值,所以可以通過返回另一個承諾來異步獲得一個值,這個異步操作可以解析爲基於舊值的新值,並且父承諾將不會解析,直到新的一個解決!而且,當然,您可以從if-else分支中返回這些承諾。

上面例子中說明的另一個非常好的事情是承諾(至少符合Promises/A +的承諾)以同樣類似的方式處理異常。第一個錯誤「引發」繞過了非錯誤回調,並跳出第一個可用的錯誤回調,很像try-catch塊。

對於它的價值,我認爲試圖用手工製作的Node.js風格的回調和async庫來模仿這種行爲是它自己特殊的地獄:)。

遵循這些準則的代碼將成爲(假設所有的功能都是異步和返回的承諾):

beginTransaction().then(function() { 
    // beginTransaction() has run 
    return updateUsers(); // resolves the boolean value `updated` 
}).then(function(updated) { 
    // updateUsers() has "returned" `updated` 
    if(updated) { 
    if(isBusiness) { 
     return updateBusiness().then(function(updated) { 
     if(!updated) { 
      return insertBusiness(); 
     } 
     // It's okay if we don't return anything -- it will 
     // result in a promise which immediately resolves to 
     // `undefined`, which is a no-op, just like a missing 
     // else-branch 
     }); 
    } else { 
     return deleteBusiness(); 
    } 
    } else { 
    if(upsert) { 
     return insertUser().then(function() { 
     if(isBusiness) { 
      return insertBusiness(); 
     } 
     }); 
    } 
    } 
}).then(function() { 
    return commitTransaction(); 
}).done(function() { 
    console.log('all done!'); 
}, function(err) { 
    console.error(err); 
}); 
+0

非常感謝@mooiamaduck。這正是我所期望的。我已經理解它是如何工作的,但不幸的是,在我看來,它仍然很難閱讀。我發現製作一個回調函數並在開關/工作區使用標誌更容易理解。 – Eduardo

+0

我覺得這更自然,所以我認爲這是一個味道問題。 – mooiamaduck

+0

讓我感到害怕的是非承諾代碼需要轉化爲承諾代碼來與其他承諾代碼一起工作。這意味着只要你開始使用一些承諾,它們就會流失你的整個代碼庫,直到你寫的所有東西都是基於promise的。這並不一定是壞事,但是如果你遇到了一個死衚衕,那麼由於某種原因,代碼根本不可能基於某種原因被承諾,這是非常可怕的。也許這不會發生? –