1. 程式人生 > >es6 async函式與其他非同步處理方法的比較

es6 async函式與其他非同步處理方法的比較

async函式與其他非同步處理方法的比較

我們通過一個例子,來看 async函式與 Promise、Generator函式的比較。

假定某個 DOM 元素上面,部署了一系列的動畫,前一個動畫結束,才能開始後一個。如果當中有一個動畫出錯,就不再往下執行,返回上一個成功執行的動畫的返回值。

首先是 ES6 Promise 的寫法。

  1. function chainAnimationsPromise(elem, animations){
  2. // 變數ret用來儲存上一個動畫的返回值
  3. let ret =null;
  4. // 新建一個空的Promise
  5. let p =Promise.resolve();
  6. // 使用then方法,新增所有動畫
  7. for(let anim of animations){
  8. p = p.then(function(val){
  9. ret = val;
  10. return anim(elem);
  11. });
  12. }
  13. // 返回一個部署了錯誤捕捉機制的Promise
  14. return p.catch(function(e){
  15. /* 忽略錯誤,繼續執行 */
  16. }).then(function(){
  17. return ret;
  18. });
  19. }

雖然 Promise 的寫法比回撥函式的寫法大大改進,但是一眼看上去,程式碼完全都是 Promise 的 API(thencatch等等),操作本身的語義反而不容易看出來。

接著是 Generator函式的寫法。

  1. function chainAnimationsGenerator(elem, animations){
  2. return spawn(function*(){
  3. let ret =null;
  4. try{
  5. for(let anim of animations){
  6. ret =yield anim(elem);
  7. }
  8. }catch(e){
  9. /* 忽略錯誤,繼續執行 */
  10. }
  11. return ret;
  12. });
  13. }

上面程式碼使用 Generator函式遍歷了每個動畫,語義比 Promise 寫法更清晰,使用者定義的操作全部都出現在spawn函式的內部。這個寫法的問題在於,必須有一個任務執行器,自動執行 Generator函式,上面程式碼的spawn

函式就是自動執行器,它返回一個 Promise 物件,而且必須保證yield語句後面的表示式,必須返回一個 Promise。

最後是 async函式的寫法。

  1. async function chainAnimationsAsync(elem, animations){
  2. let ret =null;
  3. try{
  4. for(let anim of animations){
  5. ret = await anim(elem);
  6. }
  7. }catch(e){
  8. /* 忽略錯誤,繼續執行 */
  9. }
  10. return ret;
  11. }

可以看到 Async 函式的實現最簡潔,最符合語義,幾乎沒有語義不相關的程式碼。它將 Generator 寫法中的自動執行器,改在語言層面提供,不暴露給使用者,因此程式碼量最少。如果使用 Generator 寫法,自動執行器需要使用者自己提供。