js異同步

前言

回調地獄
imagejavascript

js異步

Javascript 語言的執行環境是「單線程」(single thread)。所謂「單線程」,就是指一次只能完成一件任務。若是有多個任務,就必須排隊,前面一個任務完成,再執行後面一個任務。html

這種模式的好處是實現起來比較簡單,執行環境相對單純;壞處是隻要有一個任務耗時很長,後面的任務都必須排隊等着,會拖延整個程序的執行。常見的瀏覽器無響應(假死),每每就是由於某一段 JavaScript 代碼長時間運行(好比死循環),致使整個頁面卡在這個地方,其餘任務沒法執行。java

爲了解決這個問題,Javascript 語言將任務的執行模式分紅兩種:同步(Synchronous)和異步(Asynchronous)。數組

回調函數

假定有兩個函數f1和f2,後者必須等到前者執行完成,才能執行。這時,能夠考慮改寫f1,把f2寫成f1的回調函數。promise

function f1(callback) {
      callback();
  }

事件監聽

f1.on('done', f2);

function f1(){
  setTimeout(function () {
    // f1的任務代碼
    f1.trigger('done');
  }, 1000);
}

發佈訂閱

jQuery.subscribe("done", f2);

function f1(){
    setTimeout(function () {
        // f1的任務代碼
        jQuery.publish("done");
    }, 1000);
}

jQuery.unsubscribe("done", f2);

這種方法的性質與」事件監聽」相似,可是明顯優於後者。由於咱們能夠經過查看」消息中心」,瞭解存在多少信號、每一個信號有多少訂閱者,從而監控程序的運行。瀏覽器

js異步操做流程控制

若是有多個異步操做,就存在一個流程控制的問題:肯定操做執行的順序,之後如何保證遵照這種順序異步

function async(arg, callback) {
  console.log('參數爲 ' + arg +' , 1秒後返回結果');
  setTimeout(function() { callback(arg * 2); }, 1000);
}

上面代碼的async函數是一個異步任務,很是耗時,每次執行須要1秒才能完成,而後再調用回調函數。async

若是有6個這樣的異步任務,須要所有完成後,才能執行下一步的final函數。函數

function final(value) {
  console.log('完成: ', value);
}

請問應該如何安排操做流程?spa

async(1, function(value){
  async(value, function(value){
    async(value, function(value){
      async(value, function(value){
        async(value, function(value){
          async(value, final);
        });
      });
    });
  });
});

上面代碼採用6個回調函數的嵌套,不只寫起來麻煩,容易出錯,並且難以維護

串行執行

咱們能夠編寫一個流程控制函數,讓它來控制異步任務,一個任務完成之後,再執行另外一個。這就叫串行執行。(任務隊列)

let taskQueen = [1, 2, 3, 4, 5, 6];
 let result = [];
 function invoke(curTask) {
    if (curTask) {
      console.log('當前正在執行任務', curTask);
      result.push(curTask + '完成');
    } else {
      console.log('當前任務所有完成');
    }
 }
 invoke(taskQueen.shift());
var items = [ 1, 2, 3, 4, 5, 6 ];
var results = [];
function series(item) {
  if(item) {
    async( item, function(result) {
      results.push(result);
      return series(items.shift());
    });
  } else {
    return final(results);
  }
}
series(items.shift());

並行

var items = [ 1, 2, 3, 4, 5, 6 ];
    var results = [];
    items.forEach(function(item) {
      async(item, function(result){
        results.push(result);
        if(results.length == items.length) {
          final(results);
        }
      })
    });

上面代碼中,forEach方法會同時發起6個異步任務,等到它們所有完成之後,纔會執行final函數。

並行執行的好處是效率較高,比起串行執行一次只能執行一個任務,較爲節約時間。可是問題在於若是並行的任務較多,很容易耗盡系統資源,拖慢運行速度。所以有了第三種流程控制方式

function launcher() {
  while(running < limit && items.length > 0) {
    var item = items.shift();
    async(item, function(result) {
      results.push(result);
      running--;
      if(items.length > 0) {
        launcher();
      } else if(running == 0) {
        final(results);
      }
    });
    running++;
  }
}

launcher();

promise

Promise 對象用於一個異步操做的最終完成(或失敗)及其結果值的表示。(簡單點說就是處理異步請求。咱們常常會作些承諾,若是我贏了你就嫁給我,若是輸了我就嫁給你之類的諾言。這就是promise的中文含義:諾言,一個成功,一個失敗。) -MDN

new Promise(
    /* executor */
    function(resolve, reject) {...}
);

一個 Promise有如下幾種狀態:

  • pending: 初始狀態,不是成功或失敗狀態。
  • fulfilled: 意味着操做成功完成。
  • rejected: 意味着操做失敗。

then()

var promise = new Promise(function(resolve, reject){
      resolve("傳遞給then的值");
  });
  promise.then(function (value) {
      console.log(value);
  }, function (error) {
      console.error(error);
  });

catch()

捕獲promise 運行的各類錯誤 promise.then(undefined, onRejected)
的語法糖

var promise = new Promise(function(resolve, reject){
    resolve("傳遞給then的值");
});
promise.then(function (value) {
    console.log(value);
}).catch(function (error) {
    console.error(error);
});

Promise.resolve && Promise.reject

Promise.all

生成並返回一個新的promise對象。

參數傳遞promise數組中全部的promise對象都變爲resolve的時候,該方法纔會返回, 新建立的promise則會使用這些promise的值。

若是參數中的任何一個promise爲reject的話,則整個Promise.all調用會當即終止,並返回一個reject的新的promise對象。

因爲參數數組中的每一個元素都是由 Promise.resolve 包裝(wrap)的,因此Paomise.all能夠處理不一樣類型的promose對象。

var p1 = Promise.resolve(1),
    p2 = Promise.resolve(2),
    p3 = Promise.resolve(3);
Promise.all([p1, p2, p3]).then(function (results) {
    console.log(results);  // [1, 2, 3]
});

Promise.race

var p1 = Promise.resolve(1),
    p2 = Promise.resolve(2),
    p3 = Promise.resolve(3);
Promise.race([p1, p2, p3]).then(function (value) {
    console.log(value);  // 1
});

生成並返回一個新的promise對象。

參數 promise 數組中的任何一個promise對象若是變爲resolve或者reject的話, 該函數就會返回,並使用這個promise對象的值進行resolve或者reject。

參考

JavaScript Promise迷你書(中文版)

promise阮一峯(http://javascript.ruanyifeng....

相關文章
相關標籤/搜索