promise對象

《JavaScript 標準參考教程(alpha)》by阮一峯
ES6原生提供了Promise對象。能夠將異步操做以同步操做的流程表達出來,避免了層層嵌套的回調函數。 javascript

爲何要使用Promise

Javascript的異步執行

javascript語言的執行環境是單線程的,即一次只能執行一個任務,若是有多個任務,就必須等待前面的任務執行完後才能執行下一個任務,若是前一個任務耗時很長,就會陷入阻塞,瀏覽器呈「假死」狀態。好比,發送一個請求,等待請求返回的過程當中這段時間就沒有執行任何一個任務,白白佔用了進程。php

針對這種狀況,javascript提出了同步和異步兩種模式。html

同步模式

即傳統做法,每一個任務按順序執行,程序的執行順序和任務的排列順序是一致的。java

異步模式

每個任務分紅兩段,第一段代碼包含對外部數據的請求,第二段代碼被寫成一個回調函數,包含了對外部數據的處理。jquery

第一段代碼執行完,不是馬上執行第二段代碼,而是將程序的執行權交給第二個任務。等到外部數據返回了,再由系統通知執行第二段代碼。ajax

因此,程序的執行順序與任務的排列順序是不一致的、異步的。編程

異步編程的幾種方法

回調函數

var f2 = function(){
        console.log('f2');
    }

    var f1 = function(callback) {
        console.log('f1');
        setTimeout(function(){
            callback();
        },1000);
    }

    f1(f2);
    console.log('外部');

結果:
f1
外部
f2json

優勢:簡單、容易理解和部署
缺點:不利於代碼的閱讀和維護,各個部分之間高度耦合,使程序結構混亂,流程難以追蹤,並且每一個任務只能指定一個回調函數promise

事件監聽:事件驅動模式

使用jquery來舉例瀏覽器

f1.on('done', f2);
    
    function f1(){
      setTimeout(function () {
        // f1的任務代碼
        f1.trigger('done'); //f1的代碼執行完成後當即執行done事件
      }, 1000);
    }

優勢:比較容易理解,能夠綁定多個事件,每一個事件能夠指定多個回調函數,並且能夠」去耦合「,有利於實現模塊化。
缺點:整個程序都要變成事件驅動型,運行流程會變得很不清晰。

發佈/訂閱:觀察者模式

「事件」能夠理解成」信號」,若是存在一個」信號中心」,某個任務執行完成,就向信號中心」發佈」一個信號,其餘任務能夠向信號中心」訂閱」(subscribe)這個信號,從而知道何時本身能夠開始執行。這就叫作」發佈/訂閱模式「,又稱」觀察者模式「。
具體方法略。可以使用jquery的插件

異步操做的流程控制

咱們提到異步模式的程序執行順序與任務排列順序並不肯定是否一致,取決於向外部請求數據所耗費的時間長短。爲了能控制順序,有如下的方法:
ps:下面說的串行和並行針對的是對於異步任務的執行順序,而上面說的同步、異步指的是異步仍是同步

串行執行

function async(item, callback){
    setTimeout(function(){
         callback(item);
    },1000);
}
function final() {
    console.log(results);
}
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());

結果:
[1, 2, 3, 4, 5, 6]
async就是咱們的異步任務,從結果中能夠看出來,的確按照了咱們的任務排列順序執行了。也能夠看做把異步任務按同步模式來執行。

並行執行

var a=1; //用來標記異步任務第一部分的執行順序
var b=1; //用來標記異步任務第二部分的執行順序
function async(item, callback){
    console.log(a);
    a++;
    setTimeout(function(){
        console.log('回調任務',b);
        b++;
         callback(item);
    },2000);
}
function final() {
    console.log(results);
}
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();
    }
  })
});

結果:
1
2
3
4
5
6
回調任務1
回調任務2
回調任務3
回調任務4
回調任務5
回調任務6
[1,2,3,4,5,6]
異步執行的意思是,所有任務同時進行,等全部異步任務都執行完後才執行final函數。
但這樣雖然加快了速度,卻存在任務過多而形成資源消耗大,運行速度過慢的問題,所以咱們須要控制一次同時執行任務的個數,也就是串行和並行相結合。

串行、並行相結合

var items = [ 1, 2, 3, 4, 5, 6 ];
var results = [];
var running = 0;
var limit = 2;

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();
      }
    });
    running++;
  }
}

launcher();

promise對象

爲了用更簡潔的代碼實現上述異步編程的執行順序,ES6就推出了promise對象

三種狀態:未完成、已完成、失敗
最終結果:成功——>傳回一個值;失敗——>拋出一個錯誤

var promise = new Promise(function(resolve, reject) {
  // 異步操做的代碼

  if (/* 異步操做成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});


var po = new Promise();
po.then(function(value) {
  // success
}, function(value) {
  // failure
});
若是省略then的第二個參數,則僅表明成功後的回調函數。

po.then(step1).then(step2).then(step3)鏈式使用,一旦前面有一步錯誤,後面都不會執行

promise與ajax相結合

參考jquery的promise

jquery的ajax

以前的寫法:

$.ajax({
    url: 'test.php', //請求地址
    data: {'v': v},  //發送的數據
    dataType: 'json',//返回的數據格式
    type: 'get',     //請求的方式:get|post
    
    //發送前執行
    beforeSend: function() {
        console.log('beforeSend');
    },
    
    //返回成功執行
    success: function(result) {
        console.log(result);
    },
    
    //返回失敗執行
    error: function(err) {
        console.log(err);
    },
    
    //不管成功仍是失敗都會執行
    complete: function() {
        console.log('complete');
    },
    
    //狀態碼對應的操做
    statusCode: {
        200: function() {},
        404: function() {}
    }
})
結合實際選擇須要的選項

如今的ajax——Promise/Deferred模式

$.ajax({url: 'test.php'})
      .success(function() {})
      .error(function() {})
      .success(function() {})

$.ajax()返回的就是promise對象
1.8版本後,jquery使用done(),fail()和always()分別對應着前面的三個方法

$.ajax({url: 'test.php'})
      .done(function() {})
      .fail(function() {})
      .always(function() {}) 
   能夠用then()方法把done()和fail()合併在一塊兒
   promise.then(function() {
       //done
   }, function() {
       //fail
   })
   若是隻有一個參數,就表示done()方法

$.when()

$.when(deferreds):提供一種方法來執行一個或多個對象的回調函數,Deferred(延遲)對象一般表示異步事件

$.when( $.ajax({url: 'test.php'}) )
    .then(function(data, status, jqXHR) {})

$.when(
    $.ajax({url:'test.php'}),
    $.ajax({url:'test1.php'})
    )
    .done(function(result, result1){
        console.log(result);
        console.log(result1);
    })
    .fail(function(err){
        console.log("error");
    })
當兩個ajax都執行成功時就會調用done方法,不然只要其中一個失敗就會執行fail方法。
相關文章
相關標籤/搜索