国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 編程 > JavaScript > 正文

詳解JavaScript異步編程中jQuery的promise對(duì)象的作用

2019-11-20 10:10:22
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

Promise, 中文可以理解為愿望,代表單個(gè)操作完成的最終結(jié)果。一個(gè)Promise擁有三種狀態(tài):分別是unfulfilled(未滿足的)、fulfilled(滿足的)、failed(失敗的),fulfilled狀態(tài)和failed狀態(tài)都可以被監(jiān)聽(tīng)。一個(gè)愿望可以從未滿足狀態(tài)變?yōu)闈M足或者失敗狀態(tài),一旦一個(gè)愿望處于滿足或者失敗狀態(tài),其狀態(tài)將不可再變化。這種“不可改變”的特性對(duì)于一個(gè)Promise來(lái)說(shuō)非常的重要,它可以避免Promise的狀態(tài)監(jiān)聽(tīng)器修改一個(gè)Promise的狀態(tài)導(dǎo)致別的監(jiān)聽(tīng)器的行為異常。例如:一個(gè)監(jiān)聽(tīng)fulfilled狀態(tài)的監(jiān)聽(tīng)器把Promise的狀態(tài)修改為failed,那么將觸發(fā)failed狀態(tài)的監(jiān)聽(tīng)器,而如果一個(gè)failed狀態(tài)監(jiān)聽(tīng)器又把Promise的狀態(tài)設(shè)置為fulfilled,那么又將觸發(fā)fulfilled狀態(tài)的監(jiān)聽(tīng)器,這樣將導(dǎo)致死循環(huán)。另外一種理解Promise這種特性的方式是把Promise看成是javascript中的primative類型的變量,這種變量可以被傳入被調(diào)用的函數(shù)中,但是不可以被調(diào)用函數(shù)所改變。

每一個(gè)Promise對(duì)象都有一個(gè)方法:then(fulfilledHandler, errorHandler, progressHandler),用于監(jiān)聽(tīng)一個(gè)Promise的不同狀態(tài)。fulfilledHandler用于監(jiān)聽(tīng)fulfilled事件,errorHandler用于監(jiān)聽(tīng)failed事件,progressHandler用于監(jiān)聽(tīng)progress事件。一個(gè)Promise不強(qiáng)制實(shí)現(xiàn)progress狀態(tài)的事件監(jiān)聽(tīng)(jQuery的Deferred就是一個(gè)Promise的實(shí)現(xiàn),但沒(méi)有實(shí)現(xiàn)對(duì)progress狀態(tài)事件的處理)。

then(...)函數(shù)中的fulfilledHandler和errorHandler的返回值是一個(gè)新的Promise對(duì)象, 以便能夠鏈?zhǔn)秸{(diào)用then(...)函數(shù)。每一個(gè)回調(diào)函數(shù)在正常情況下返回的是處于fulfilled狀態(tài)的Promise,如果該回調(diào)函數(shù)返回錯(cuò)誤值,那么返回的Promise狀態(tài)將會(huì)變?yōu)閒ailed。

promise在異步編程中的作用

異步模式在web編程中變得越來(lái)越重要,對(duì)于web主流語(yǔ)言Javascript來(lái)說(shuō),這種模式實(shí)現(xiàn)起來(lái)不是很利索,為此,許多Javascript庫(kù)(比如 jQuery和Dojo)添加了一種稱為promise的抽象(有時(shí)也稱之為deferred)。通過(guò)這些庫(kù),開(kāi)發(fā)人員能夠在實(shí)際編程中使用 promise模式。
隨著Web 2.0技術(shù)的深入,瀏覽器端承受了越來(lái)越多的計(jì)算壓力,所以“并發(fā)”具有積極的意義。對(duì)于開(kāi)發(fā)人員來(lái)說(shuō),既要保持頁(yè)面與用戶的交互不受影響,又要協(xié)調(diào)頁(yè)面與異步任務(wù)的關(guān)系,這種非線性執(zhí)行的編程要求存在適應(yīng)的困難。先拋開(kāi)頁(yè)面交互不談,我們能夠想到對(duì)于異步調(diào)用需要處理兩種結(jié)果――成功操作和失敗處理。在成功的調(diào)用后,我們可能需要把返回的結(jié)果用在另一個(gè)Ajax請(qǐng)求中,這就會(huì)出現(xiàn)“函數(shù)連環(huán)套”的情況。這種情況會(huì)造成編程的復(fù)雜性。看看下面的代碼示例(基于XMLHttpRequest2):

function searchTwitter(term, onload, onerror) {    var xhr, results, url;   url = 'http://search.twitter.com/search.json?rpp=100&q=' + term;   xhr = new XMLHttpRequest();   xhr.open('GET', url, true);    xhr.onload = function (e) {     if (this.status === 200) {       results = JSON.parse(this.responseText);       onload(results);     }   };    xhr.onerror = function (e) {     onerror(e);   };    xhr.send(); }  function handleError(error) {   /* handle the error */ }  function concatResults() {   /* order tweets by date */ }  function loadTweets() {   var container = document.getElementById('container');    searchTwitter('#IE10', function (data1) {     searchTwitter('#IE9', function (data2) {       /* Reshuffle due to date */       var totalResults = concatResults(data1.results, data2.results);       totalResults.forEach(function (tweet) {         var el = document.createElement('li');         el.innerText = tweet.text;         container.appendChild(el);       });     }, handleError);   }, handleError); }

上面的代碼其功能是獲取Twitter中hashtag為IE10和IE9的內(nèi)容并在頁(yè)面中顯示出來(lái)。這種嵌套的回調(diào)函數(shù)難以理解,開(kāi)發(fā)人員需要仔細(xì)分析哪些代碼用于應(yīng)用的業(yè)務(wù)邏輯,而哪些代碼處理異步函數(shù)調(diào)用的,代碼結(jié)構(gòu)支離破碎。錯(cuò)誤處理也分解了,我們需要在各個(gè)地方檢測(cè)錯(cuò)誤的發(fā)生并作出相應(yīng)的處理。

為了降低異步編程的復(fù)雜性,開(kāi)發(fā)人員一直尋找簡(jiǎn)便的方法來(lái)處理異步操作。其中一種處理模式稱為promise,它代表了一種可能會(huì)長(zhǎng)時(shí)間運(yùn)行而且不一定必須完整的操作的結(jié)果。這種模式不會(huì)阻塞和等待長(zhǎng)時(shí)間的操作完成,而是返回一個(gè)代表了承諾的(promised)結(jié)果的對(duì)象。

考慮這樣一個(gè)例子,頁(yè)面代碼需要訪問(wèn)第三方的API,網(wǎng)絡(luò)延遲可能會(huì)造成響應(yīng)時(shí)間較長(zhǎng),在這種情況下,采用異步編程不會(huì)影響整個(gè)頁(yè)面與用戶的交互。promise模式通常會(huì)實(shí)現(xiàn)一種稱為then的方法,用來(lái)注冊(cè)狀態(tài)變化時(shí)對(duì)應(yīng)的回調(diào)函數(shù)。比如下面的代碼示例:

searchTwitter(term).then(filterResults).then(displayResults);

promise模式在任何時(shí)刻都處于以下三種狀態(tài)之一:未完成(unfulfilled)、已完成(resolved)和拒絕(rejected)。以CommonJS Promise/A 標(biāo)準(zhǔn)為例,promise對(duì)象上的then方法負(fù)責(zé)添加針對(duì)已完成和拒絕狀態(tài)下的處理函數(shù)。then方法會(huì)返回另一個(gè)promise對(duì)象,以便于形成promise管道,這種返回promise對(duì)象的方式能夠支持開(kāi)發(fā)人員把異步操作串聯(lián)起來(lái),如then(resolvedHandler, rejectedHandler); 。resolvedHandler 回調(diào)函數(shù)在promise對(duì)象進(jìn)入完成狀態(tài)時(shí)會(huì)觸發(fā),并傳遞結(jié)果;rejectedHandler函數(shù)會(huì)在拒絕狀態(tài)下調(diào)用。

有了promise模式,我們可以重新實(shí)現(xiàn)上面的Twitter示例。為了更好的理解實(shí)現(xiàn)方法,我們嘗試著從零開(kāi)始構(gòu)建一個(gè)promise模式的框架。首先需要一些對(duì)象來(lái)存儲(chǔ)promise。

var Promise = function () {    /* initialize promise */  };

接下來(lái),定義then方法,接受兩個(gè)參數(shù)用于處理完成和拒絕狀態(tài)。

Promise.prototype.then = function (onResolved, onRejected) {   /* invoke handlers based upon state transition */ };

同時(shí)還需要兩個(gè)方法來(lái)執(zhí)行理從未完成到已完成和從未完成到拒絕的狀態(tài)轉(zhuǎn)變。

Promise.prototype.resolve = function (value) {   /* move from unfulfilled to resolved */ };  Promise.prototype.reject = function (error) {   /* move from unfulfilled to rejected */ };

現(xiàn)在搭建了一個(gè)promise的架子,我們可以繼續(xù)上面的示例,假設(shè)只獲取IE10的內(nèi)容。創(chuàng)建一個(gè)方法來(lái)發(fā)送Ajax請(qǐng)求并將其封裝在promise中。這個(gè)promise對(duì)象分別在xhr.onload和xhr.onerror中指定了完成和拒絕狀態(tài)的轉(zhuǎn)變過(guò)程,請(qǐng)注意searchTwitter函數(shù)返回的正是promise對(duì)象。然后,在loadTweets中,使用then方法設(shè)置完成和拒絕狀態(tài)對(duì)應(yīng)的回調(diào)函數(shù)。

function searchTwitter(term) {  var url, xhr, results, promise;  url = 'http://search.twitter.com/search.json?rpp=100&q=' + term;  promise = new Promise();  xhr = new XMLHttpRequest();  xhr.open('GET', url, true);  xhr.onload = function (e) {    if (this.status === 200) {      results = JSON.parse(this.responseText);      promise.resolve(results);    }  };  xhr.onerror = function (e) {    promise.reject(e);  };  xhr.send();  return promise;}function loadTweets() {  var container = document.getElementById('container');  searchTwitter('#IE10').then(function (data) {    data.results.forEach(function (tweet) {      var el = document.createElement('li');      el.innerText = tweet.text;      container.appendChild(el);    });  }, handleError);}

到目前為止,我們可以把promise模式應(yīng)用于單個(gè)Ajax請(qǐng)求,似乎還體現(xiàn)不出promise的優(yōu)勢(shì)來(lái)。下面來(lái)看看多個(gè)Ajax請(qǐng)求的并發(fā)協(xié)作。此時(shí),我們需要另一個(gè)方法when來(lái)存儲(chǔ)準(zhǔn)備調(diào)用的promise對(duì)象。一旦某個(gè)promise從未完成狀態(tài)轉(zhuǎn)化為完成或者拒絕狀態(tài),then方法里對(duì)應(yīng)的處理函數(shù)就會(huì)被調(diào)用。when方法在需要等待所有操作都完成的時(shí)候至關(guān)重要。

Promise.when = function () {  /* handle promises arguments and queue each */};

以剛才獲取IE10和IE9兩塊內(nèi)容的場(chǎng)景為例,我們可以這樣來(lái)寫(xiě)代碼:

var container, promise1, promise2;container = document.getElementById('container');promise1 = searchTwitter('#IE10');promise2 = searchTwitter('#IE9');Promise.when(promise1, promise2).then(function (data1, data2) {  /* Reshuffle due to date */  var totalResults = concatResults(data1.results, data2.results);  totalResults.forEach(function (tweet) {    var el = document.createElement('li');    el.innerText = tweet.text;    container.appendChild(el);  });}, handleError);

分析上面的代碼可知,when函數(shù)會(huì)等待兩個(gè)promise對(duì)象的狀態(tài)發(fā)生變化再做具體的處理。在實(shí)際的Promise庫(kù)中,when函數(shù)有很多變種,比如 when.some()、when.all()、when.any()等,讀者從函數(shù)名字中大概能猜出幾分意思來(lái),詳細(xì)的說(shuō)明可以參考CommonJS的一個(gè)promise實(shí)現(xiàn)when.js。

除了CommonJS,其他主流的Javascript框架如jQuery、Dojo等都存在自己的promise實(shí)現(xiàn)。開(kāi)發(fā)人員應(yīng)該好好利用這種模式來(lái)降低異步編程的復(fù)雜性。我們選取Dojo為例,看一看它的實(shí)現(xiàn)有什么異同。

Dojo框架里實(shí)現(xiàn)promise模式的對(duì)象是Deferred,該對(duì)象也有then函數(shù)用于處理完成和拒絕狀態(tài)并支持串聯(lián),同時(shí)還有resolve和reject,功能如之前所述。下面的代碼完成了Twitter的場(chǎng)景:

function searchTwitter(term) {  var url, xhr, results, def;  url = 'http://search.twitter.com/search.json?rpp=100&q=' + term;  def = new dojo.Deferred();  xhr = new XMLHttpRequest();  xhr.open('GET', url, true);  xhr.onload = function (e) {    if (this.status === 200) {      results = JSON.parse(this.responseText);      def.resolve(results);    }  };  xhr.onerror = function (e) {    def.reject(e);  };  xhr.send();  return def;}dojo.ready(function () {  var container = dojo.byId('container');  searchTwitter('#IE10').then(function (data) {    data.results.forEach(function (tweet) {      dojo.create('li', {        innerHTML: tweet.text      }, container);    });  });});

不僅如此,類似dojo.xhrGet方法返回的即是dojo.Deferred對(duì)象,所以無(wú)須自己包裝promise模式。

var deferred = dojo.xhrGet({  url: "search.json",  handleAs: "json"});deferred.then(function (data) {  /* handle results */}, function (error) {  /* handle error */});

除此之外,Dojo還引入了dojo.DeferredList,支持開(kāi)發(fā)人員同時(shí)處理多個(gè)dojo.Deferred對(duì)象,這其實(shí)就是上面所提到的when方法的另一種表現(xiàn)形式。

dojo.require("dojo.DeferredList");dojo.ready(function () {  var container, def1, def2, defs;  container = dojo.byId('container');  def1 = searchTwitter('#IE10');  def2 = searchTwitter('#IE9');  defs = new dojo.DeferredList([def1, def2]);  defs.then(function (data) {    // Handle exceptions    if (!results[0][0] || !results[1][0]) {      dojo.create("li", {        innerHTML: 'an error occurred'      }, container);      return;    }    var totalResults = concatResults(data[0][1].results, data[1][1].results);    totalResults.forEach(function (tweet) {      dojo.create("li", {        innerHTML: tweet.text      }, container);    });  });});

上面的代碼比較清楚,不再詳述。

說(shuō)到這里,讀者可能已經(jīng)對(duì)promise模式有了一個(gè)比較完整的了解,異步編程會(huì)變得越來(lái)越重要,在這種情況下,我們需要找到辦法來(lái)降低復(fù)雜度,promise模式就是一個(gè)很好的例子,它的風(fēng)格比較人性化,而且主流的JS框架提供了自己的實(shí)現(xiàn)。所以在編程實(shí)踐中,開(kāi)發(fā)人員應(yīng)該嘗試這種便捷的編程技巧。需要注意的是,promise模式的使用需要恰當(dāng)?shù)卦O(shè)置promise對(duì)象,在對(duì)應(yīng)的事件中調(diào)用狀態(tài)轉(zhuǎn)換函數(shù),并且在最后返回promise對(duì)象。


發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 西丰县| 三穗县| 偏关县| 屏南县| 靖边县| 酉阳| 偏关县| 浦县| 南召县| 高阳县| 蛟河市| 黄冈市| 伊宁市| 抚顺市| 新密市| 平南县| 田东县| 临沂市| 永州市| 舞阳县| 辰溪县| 汾阳市| 荃湾区| 资源县| 扬中市| 中宁县| 米易县| 沂水县| 惠水县| 景东| 汪清县| 合川市| 连城县| 台江县| 莫力| 广西| 云浮市| 抚松县| 苗栗县| 揭西县| 扬中市|