欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JavaScript Promise啟示錄

 更新時(shí)間:2014年08月12日 15:18:13   投稿:mdxy-dxy  
近幾年隨著JavaScript開(kāi)發(fā)模式的逐漸成熟,CommonJS規(guī)范順勢(shì)而生,其中就包括提出了Promise規(guī)范,Promise完全改變了js異步編程的寫(xiě)法,讓異步編程變得十分的易于理解

本篇,主要普及promise的用法。

一直以來(lái),JavaScript處理異步都是以callback的方式,在前端開(kāi)發(fā)領(lǐng)域callback機(jī)制幾乎深入人心。在設(shè)計(jì)API的時(shí)候,不管是瀏覽器廠(chǎng)商還是SDK開(kāi)發(fā)商亦或是各種類(lèi)庫(kù)的作者,基本上都已經(jīng)遵循著callback的套路。

近幾年隨著JavaScript開(kāi)發(fā)模式的逐漸成熟,CommonJS規(guī)范順勢(shì)而生,其中就包括提出了Promise規(guī)范,Promise完全改變了js異步編程的寫(xiě)法,讓異步編程變得十分的易于理解。

在callback的模型里邊,我們假設(shè)需要執(zhí)行一個(gè)異步隊(duì)列,代碼看起來(lái)可能像這樣:

loadImg('a.jpg', function() {
  loadImg('b.jpg', function() {
    loadImg('c.jpg', function() {
      console.log('all done!');
    });
  });
});

這也就是我們常說(shuō)的回調(diào)金字塔,當(dāng)異步的任務(wù)很多的時(shí)候,維護(hù)大量的callback將是一場(chǎng)災(zāi)難。當(dāng)今Node.js大熱,好像很多團(tuán)隊(duì)都要用它來(lái)做點(diǎn)東西以沾沾“洋氣”,曾經(jīng)跟一個(gè)運(yùn)維的同學(xué)聊天,他們也是打算使用Node.js做一些事情,可是一想到j(luò)s的層層回調(diào)就望而卻步。

好,扯淡完畢,下面進(jìn)入正題。

Promise可能大家都不陌生,因?yàn)镻romise規(guī)范已經(jīng)出來(lái)好一段時(shí)間了,同時(shí)Promise也已經(jīng)納入了ES6,而且高版本的chrome、firefox瀏覽器都已經(jīng)原生實(shí)現(xiàn)了Promise,只不過(guò)和現(xiàn)如今流行的類(lèi)Promise類(lèi)庫(kù)相比少些API。

所謂Promise,字面上可以理解為“承諾”,就是說(shuō)A調(diào)用B,B返回一個(gè)“承諾”給A,然后A就可以在寫(xiě)計(jì)劃的時(shí)候這么寫(xiě):當(dāng)B返回結(jié)果給我的時(shí)候,A執(zhí)行方案S1,反之如果B因?yàn)槭裁丛驔](méi)有給到A想要的結(jié)果,那么A執(zhí)行應(yīng)急方案S2,這樣一來(lái),所有的潛在風(fēng)險(xiǎn)都在A(yíng)的可控范圍之內(nèi)了。

上面這句話(huà),翻譯成代碼類(lèi)似:

var resB = B();
var runA = function() {
  resB.then(execS1, execS2);
};
runA();

只看上面這行代碼,好像看不出什么特別之處。但現(xiàn)實(shí)情況可能比這個(gè)復(fù)雜許多,A要完成一件事,可能要依賴(lài)不止B一個(gè)人的響應(yīng),可能需要同時(shí)向多個(gè)人詢(xún)問(wèn),當(dāng)收到所有的應(yīng)答之后再執(zhí)行下一步的方案。最終翻譯成代碼可能像這樣:

var resB = B();
var resC = C();
...

var runA = function() {
  reqB
    .then(resC, execS2)
    .then(resD, execS3)
    .then(resE, execS4)
    ...
    .then(execS1);
};

runA();

在這里,當(dāng)每一個(gè)被詢(xún)問(wèn)者做出不符合預(yù)期的應(yīng)答時(shí)都用了不同的處理機(jī)制。事實(shí)上,Promise規(guī)范沒(méi)有要求這樣做,你甚至可以不做任何的處理(即不傳入then的第二個(gè)參數(shù))或者統(tǒng)一處理。

好了,下面我們來(lái)認(rèn)識(shí)下Promise/A+規(guī)范

  • 一個(gè)promise可能有三種狀態(tài):等待(pending)、已完成(fulfilled)、已拒絕(rejected)
  • 一個(gè)promise的狀態(tài)只可能從“等待”轉(zhuǎn)到“完成”態(tài)或者“拒絕”態(tài),不能逆向轉(zhuǎn)換,同時(shí)“完成”態(tài)和“拒絕”態(tài)不能相互轉(zhuǎn)換
  • promise必須實(shí)現(xiàn)then方法(可以說(shuō),then就是promise的核心),而且then必須返回一個(gè)promise,同一個(gè)promise的then可以調(diào)用多次,并且回調(diào)的執(zhí)行順序跟它們被定義時(shí)的順序一致
  • then方法接受兩個(gè)參數(shù),第一個(gè)參數(shù)是成功時(shí)的回調(diào),在promise由“等待”態(tài)轉(zhuǎn)換到“完成”態(tài)時(shí)調(diào)用,另一個(gè)是失敗時(shí)的回調(diào),在promise由“等待”態(tài)轉(zhuǎn)換到“拒絕”態(tài)時(shí)調(diào)用。同時(shí),then可以接受另一個(gè)promise傳入,也接受一個(gè)“類(lèi)then”的對(duì)象或方法,即thenable對(duì)象。

可以看到,Promise規(guī)范的內(nèi)容并不算多,大家可以試著自己實(shí)現(xiàn)以下Promise。

以下是筆者自己在參考許多類(lèi)Promise庫(kù)之后簡(jiǎn)單實(shí)現(xiàn)的一個(gè)Promise,代碼請(qǐng)移步promiseA。

簡(jiǎn)單分析下思路:

構(gòu)造函數(shù)Promise接受一個(gè)函數(shù)resolver,可以理解為傳入一個(gè)異步任務(wù),resolver接受兩個(gè)參數(shù),一個(gè)是成功時(shí)的回調(diào),一個(gè)是失敗時(shí)的回調(diào),這兩參數(shù)和通過(guò)then傳入的參數(shù)是對(duì)等的。

其次是then的實(shí)現(xiàn),由于Promise要求then必須返回一個(gè)promise,所以在then調(diào)用的時(shí)候會(huì)新生成一個(gè)promise,掛在當(dāng)前promise的_next上,同一個(gè)promise多次調(diào)用都只會(huì)返回之前生成的_next。

由于then方法接受的兩個(gè)參數(shù)都是可選的,而且類(lèi)型也沒(méi)限制,可以是函數(shù),也可以是一個(gè)具體的值,還可以是另一個(gè)promise。下面是then的具體實(shí)現(xiàn):

Promise.prototype.then = function(resolve, reject) {
  var next = this._next || (this._next = Promise());
  var status = this.status;
  var x;

  if('pending' === status) {
    isFn(resolve) && this._resolves.push(resolve);
    isFn(reject) && this._rejects.push(reject);
    return next;
  }

  if('resolved' === status) {
    if(!isFn(resolve)) {
      next.resolve(resolve);
    } else {
      try {
        x = resolve(this.value);
        resolveX(next, x);
      } catch(e) {
        this.reject(e);
      }
    }
    return next;
  }

  if('rejected' === status) {
    if(!isFn(reject)) {
      next.reject(reject);
    } else {
      try {
        x = reject(this.reason);
        resolveX(next, x);
      } catch(e) {
        this.reject(e);
      }
    }
    return next;
  }
};

 

這里,then做了簡(jiǎn)化,其他promise類(lèi)庫(kù)的實(shí)現(xiàn)比這個(gè)要復(fù)雜得多,同時(shí)功能也更多,比如還有第三個(gè)參數(shù)——notify,表示promise當(dāng)前的進(jìn)度,這在設(shè)計(jì)文件上傳等時(shí)很有用。對(duì)then的各種參數(shù)的處理是最復(fù)雜的部分,有興趣的同學(xué)可以參看其他類(lèi)Promise庫(kù)的實(shí)現(xiàn)。

在then的基礎(chǔ)上,應(yīng)該還需要至少兩個(gè)方法,分別是完成promise的狀態(tài)從pending到resolved或rejected的轉(zhuǎn)換,同時(shí)執(zhí)行相應(yīng)的回調(diào)隊(duì)列,即resolve()reject()方法。

到此,一個(gè)簡(jiǎn)單的promise就設(shè)計(jì)完成了,下面簡(jiǎn)單實(shí)現(xiàn)下兩個(gè)promise化的函數(shù):

function sleep(ms) {
  return function(v) {
    var p = Promise();

    setTimeout(function() {
      p.resolve(v);
    }, ms);

    return p;
  };
};

function getImg(url) {
  var p = Promise();
  var img = new Image();

  img.onload = function() {
    p.resolve(this);
  };

  img.onerror = function(err) {
    p.reject(err);
  };

  img.url = url;

  return p;
};

由于Promise構(gòu)造函數(shù)接受一個(gè)異步任務(wù)作為參數(shù),所以getImg還可以這樣調(diào)用:

function getImg(url) {
  return Promise(function(resolve, reject) {
    var img = new Image();

    img.onload = function() {
      resolve(this);
    };

    img.onerror = function(err) {
      reject(err);
    };

    img.url = url;
  });
};

接下來(lái)(見(jiàn)證奇跡的時(shí)刻),假設(shè)有一個(gè)BT的需求要這么實(shí)現(xiàn):異步獲取一個(gè)json配置,解析json數(shù)據(jù)拿到里邊的圖片,然后按順序隊(duì)列加載圖片,沒(méi)張圖片加載時(shí)給個(gè)loading效果

function addImg(img) {
  $('#list').find('> li:last-child').html('').append(img);
};

function prepend() {
  $('<li>')
    .html('loading...')
    .appendTo($('#list'));
};

function run() {
  $('#done').hide();
  getData('map.json')
    .then(function(data) {
      $('h4').html(data.name);

      return data.list.reduce(function(promise, item) {
        return promise
          .then(prepend)
          .then(sleep(1000))
          .then(function() {
            return getImg(item.url);
          })
          .then(addImg);
      }, Promise.resolve());
    })
    .then(sleep(300))
    .then(function() {
      $('#done').show();
    });
};

$('#run').on('click', run);

這里的sleep只是為了看效果加的,可猛擊查看demo!當(dāng)然,Node.js的例子可查看這里。

在這里,Promise.resolve(v)靜態(tài)方法只是簡(jiǎn)單返回一個(gè)以v為肯定結(jié)果的promise,v可不傳入,也可以是一個(gè)函數(shù)或者是一個(gè)包含then方法的對(duì)象或函數(shù)(即thenable)。

類(lèi)似的靜態(tài)方法還有Promise.cast(promise),生成一個(gè)以promise為肯定結(jié)果的promise;

Promise.reject(reason),生成一個(gè)以reason為否定結(jié)果的promise。

我們實(shí)際的使用場(chǎng)景可能很復(fù)雜,往往需要多個(gè)異步的任務(wù)穿插執(zhí)行,并行或者串行同在。這時(shí)候,可以對(duì)Promise進(jìn)行各種擴(kuò)展,比如實(shí)現(xiàn)Promise.all(),接受promises隊(duì)列并等待他們完成再繼續(xù),再比如Promise.any(),promises隊(duì)列中有任何一個(gè)處于完成態(tài)時(shí)即觸發(fā)下一步操作。

標(biāo)準(zhǔn)的Promise

可參考html5rocks的這篇文章JavaScript Promises,目前高級(jí)瀏覽器如chrome、firefox都已經(jīng)內(nèi)置了Promise對(duì)象,提供更多的操作接口,比如Promise.all(),支持傳入一個(gè)promises數(shù)組,當(dāng)所有promises都完成時(shí)執(zhí)行then,還有就是更加友好強(qiáng)大的異常捕獲,應(yīng)對(duì)日常的異步編程,應(yīng)該足夠了。

第三方庫(kù)的Promise

現(xiàn)今流行的各大js庫(kù),幾乎都不同程度的實(shí)現(xiàn)了Promise,如dojo,jQuery、Zepto、when.js、Q等,只是暴露出來(lái)的大都是Deferred對(duì)象,以jQuery(Zepto類(lèi)似)為例,實(shí)現(xiàn)上面的getImg()

function getImg(url) {
  var def = $.Deferred();
  var img = new Image();

  img.onload = function() {
    def.resolve(this);
  };

  img.onerror = function(err) {
    def.reject(err);
  };

  img.src = url;

  return def.promise();
};

當(dāng)然,jQuery中,很多的操作都返回的是Deferred或promise,如animate、ajax

// animate
$('.box')
  .animate({'opacity': 0}, 1000)
  .promise()
  .then(function() {
    console.log('done');
  });

// ajax
$.ajax(options).then(success, fail);
$.ajax(options).done(success).fail(fail);

// ajax queue
$.when($.ajax(options1), $.ajax(options2))
  .then(function() {
    console.log('all done.');
  }, function() {
    console.error('There something wrong.');
  });

jQuery還實(shí)現(xiàn)了done()fail()方法,其實(shí)都是then方法的shortcut。

處理promises隊(duì)列,jQuery實(shí)現(xiàn)的是$.when()方法,用法和Promise.all()類(lèi)似。

其他類(lèi)庫(kù),這里值得一提的是when.js,本身代碼不多,完整實(shí)現(xiàn)Promise,同時(shí)支持browser和Node.js,而且提供更加豐富的API,是個(gè)不錯(cuò)的選擇。這里限于篇幅,不再展開(kāi)。

尾聲

我們看到,不管Promise實(shí)現(xiàn)怎么復(fù)雜,但是它的用法卻很簡(jiǎn)單,組織的代碼很清晰,從此不用再受callback的折磨了。

最后,Promise是如此的優(yōu)雅!但Promise也只是解決了回調(diào)的深層嵌套的問(wèn)題,真正簡(jiǎn)化JavaScript異步編程的還是Generator,在Node.js端,建議考慮Generator。

下一篇,研究下Generator。

github原文: https://github.com/chemdemo/chemdemo.github.io/issues/6

相關(guān)文章

  • JavaScript 使用 splice 方法刪除數(shù)組元素可能導(dǎo)致的問(wèn)題分析

    JavaScript 使用 splice 方法刪除數(shù)組元素可能導(dǎo)致的問(wèn)題分析

    這篇文章主要介紹了JavaScript 使用 splice 方法刪除數(shù)組元素可能導(dǎo)致的問(wèn)題分析,當(dāng)在 JavaScript 中從數(shù)組中刪除元素時(shí),使用 splice 方法時(shí)需要謹(jǐn)慎,本文給大家詳細(xì)講解,需要的朋友可以參考下
    2023-04-04
  • JavaScript的事件代理和委托實(shí)例分析

    JavaScript的事件代理和委托實(shí)例分析

    在javasript中delegate這個(gè)詞經(jīng)常出現(xiàn),看字面的意思,代理、委托。在各種框架中,也經(jīng)常能看到delegate相關(guān)的接口。這些接口又有什么特殊的用法呢?這篇文章就主要通過(guò)實(shí)例介紹一下javascript delegate的用法和原理。
    2015-03-03
  • JavaScript更改字符串的大小寫(xiě)

    JavaScript更改字符串的大小寫(xiě)

    在javascript中涉及字符串大小寫(xiě)轉(zhuǎn)換的方法有4個(gè):toLowerCase()、toLocaleLowerCase()、toUpperCase()及toLocaleUpperCase()。今天我們主要來(lái)用下toUpperCase()和toLowerCase()方法。
    2015-05-05
  • 無(wú)阻塞加載腳本分析[全]

    無(wú)阻塞加載腳本分析[全]

    script標(biāo)簽的阻塞行為會(huì)對(duì)頁(yè)面性能產(chǎn)生負(fù)面影響,大多數(shù)瀏覽器在下載或執(zhí)行腳本的同時(shí),會(huì)阻塞下載位于它之后的資源,也會(huì)阻塞渲染位于它之后的元素。
    2011-01-01
  • 一文教你用純JS實(shí)現(xiàn)一個(gè)五子棋游戲

    一文教你用純JS實(shí)現(xiàn)一個(gè)五子棋游戲

    實(shí)現(xiàn)一個(gè)五子棋游戲, 簡(jiǎn)要分析其原理, 頁(yè)面并沒(méi)有很花哨, 原理搞懂了, 后面的就是很輕松的事了,本文給大家介紹了如何用純JS實(shí)現(xiàn)一個(gè)五子棋游戲,文中通過(guò)代碼示例給大家介紹的非常詳細(xì),具有一定的參考價(jià)值,需要的朋友可以參考下
    2023-12-12
  • JavaScript獲取并更改input標(biāo)簽name屬性的方法

    JavaScript獲取并更改input標(biāo)簽name屬性的方法

    這篇文章主要介紹了JavaScript獲取并更改input標(biāo)簽name屬性的方法,涉及javascript針對(duì)表單元素屬性的相關(guān)操作技巧,需要的朋友可以參考下
    2015-07-07
  • 淺談微信JS-SDK 微信分享接口開(kāi)發(fā)(介紹版)

    淺談微信JS-SDK 微信分享接口開(kāi)發(fā)(介紹版)

    這篇文章主要介紹了淺談微信JS-SDK 微信分享接口開(kāi)發(fā)(介紹版),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-08-08
  • JS實(shí)現(xiàn)的拋物線(xiàn)運(yùn)動(dòng)效果示例

    JS實(shí)現(xiàn)的拋物線(xiàn)運(yùn)動(dòng)效果示例

    這篇文章主要介紹了JS實(shí)現(xiàn)的拋物線(xiàn)運(yùn)動(dòng)效果,結(jié)合實(shí)例形式分析了javascript拋物線(xiàn)運(yùn)動(dòng)的相關(guān)運(yùn)算與元素動(dòng)態(tài)操作實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2018-01-01
  • layui表格checkbox選擇全選樣式及功能的實(shí)例

    layui表格checkbox選擇全選樣式及功能的實(shí)例

    下面小編就為大家分享一篇layui表格checkbox選擇全選樣式及功能的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-03-03
  • BootStrap的雙日歷時(shí)間控件使用

    BootStrap的雙日歷時(shí)間控件使用

    這段時(shí)間看了下bootstrap的時(shí)間控件,發(fā)現(xiàn)使用起來(lái)還是很簡(jiǎn)單的,趁著有時(shí)間的時(shí)候整理了一下,特此分享到腳本之家平臺(tái),感興趣的朋友參考下
    2017-07-07

最新評(píng)論