更優雅的方式: JavaScript 中順序執行異步函數

火於異步

1995年,當時最流行的瀏覽器——網景中開始運行 JavaScript (最初稱爲 LiveScript)。 1996年,微軟發佈了 JScript 兼容 JavaScript。隨着網景、微軟競爭而不斷的技術更新,在 2000年先後,JavaScript 相關的技術基礎準備就緒。 隨後到 2005 年先後,以 Google 爲首開始重視使用 AJAX(即 Asynchronous JavaScript and XML),使得複雜的網頁交互體驗接近桌面應用。javascript

而後,隨着 Web 應用變得愈來愈複雜 ,JavaScript 的生態和重要性也日益提高,YUI、prototype.js、jQuery 等各類庫相應登場,隨之而來就到了 JavaScript 的繁榮期。html

2008年,Google 發佈了 JavaScript 引擎 V8 大大改善了 JavaScript 的執行速度,進一步推進了 JavaScript 的繁榮,也爲 JavaScript 進軍服務器端打下了基礎(如:Node.js)。java

順序執行異步函數

異步爲 JavaScript 帶來非阻塞等優點的同時,同時也在一些場景下帶了不便,如:順序執行異步函數,下面總結了一些經常使用的方法。node

1. "回調地獄"

隨着應用複雜度幾何式增長,咱們可能遇到下面「回調地獄」式的代碼。git

// 第一個任務
function task1 (callback) {
  setTimeout(() => {
    console.log('1', '我是第一個任務,必須第一個執行');
    callback && callback(1);
  }, 3000);
}

// 第二個任務
function task2 (callback) {
  setTimeout(() => {
    console.log('2', '我是第二個任務');
    callback && callback(2);
  }, 1000);
}

// 第三個任務
function task3 (callback) {
  setTimeout(() => {
    console.log('3', '我是第三個任務');
    callback && callback(3);
  }, 1000);
}

// 全部任務
function allTasks () {
  task1((cb1) => {
    if (cb1) {
      task2((cb2) => {
        if (cb2) {
          task3((cb3) => {
            if (cb3) {
              // 順序完成全部任務
            }
          })
        }   
      });
    }
  });
}

allTasks();

/**
 * 3秒後
 * 1 我是第一個任務,必須第一個執行
 * 1秒後
 * 2 第二個任務
 * 1秒後
 * 3 第三個任務
 */

2. Promise

爲了不「回調地獄」帶來的複雜性和不易閱讀,ES6 推出了 Promise。此次實現起來簡單多了,但還存在 Promise 中嵌套多層 Promise 的問題,彷佛又回到了相似「回調地獄」的問題上。github

new Promise(resolve => {
  setTimeout(() => {
    console.log('1', '我是第一個任務,必須第一個執行');
    resolve(1);
  }, 3000);
}).then((val) => {

  new Promise(resolve => {
    setTimeout(() => {
      console.log('2', '我是第二個任務');
      resolve(2);
    }, 1000);
  }).then(val => {
    setTimeout(() => {
      console.log('3', '我是第三個任務');
    }, 1000); 
  });

});
/**
 * 3秒後
 * 1 我是第一個任務,必須第一個執行
 * 1秒後
 * 2 第二個任務
 * 1秒後
 * 3 第三個任務
 */

3. Await、Async

確保支持,詳細見:https://caniuse.com/#search=async編程

爲了更易書寫和閱讀來實現順序執行異步函數,ES2017 新增了 awaitasync。此次書寫體驗很是的棒,就像寫同步代碼同樣完成了順序執行異步的需求。promise

/**
 * 第一個任務
 */
function task1 () {
  return new Promise(resolve => {
    setTimeout(() => {
      console.log('1', '我是第一個任務,必須第一個執行');
      resolve('done');
    }, 3000);
  });
}

/**
 * 第二個任務
 */
function task2 () {

  return new Promise(resolve => {
    setTimeout(() => {
      console.log('2', '第二個任務');
      resolve('done');
    }, 1000)
  });
}

/**
 * 第三個任務
 */
function task3 () {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('3', '第三個任務');
      reject('error');
    }, 1000);
  });
}

/**
 * 第四個任務
 */
function task4 () {
  return new Promise(resolve => {
    setTimeout(() => {
      console.log('4', '第四個任務');
      resolve('done');
    }, 2000);
  })
}

/**
 * 全部任務
 */
async function allTasks () {
  await task1();
  await task2();
  await task3();
  await task4();
}

// 執行任務
allTasks();

/**
 * 3秒後
 * 1 我是第一個任務,必須第一個執行
 * 1秒後
 * 2 第二個任務
 * 1秒後
 * 3 第三個任務
 * Uncaught (in promise) error
 */
完整案例

基於 Node.js,經過 Await 、Async、Promise 實現的順序執行異步,爬取豆瓣電影截圖並按順序一張張下載圖片。瀏覽器

參考

轉載請註明出處: http://blog.givebest.cn/javascript/2018/04/05/javascript-sync.html

相關文章
相關標籤/搜索