<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                在本小節中,作為在第[2章](http://liubin.github.io/promises-book/#ch2-promise-race)所學的?[`Promise.race`](http://liubin.github.io/promises-book/#Promise.race)?的具體例子,我們來看一下如何使用Promise.race來實現超時機制。 當然XHR有一個?[timeout](https://developer.mozilla.org/ja/docs/XMLHttpRequest/Synchronous_and_Asynchronous_Requests)?屬性,使用該屬性也可以簡單實現超時功能,但是為了能支持多個XHR同時超時或者其他功能,我們采用了容易理解的異步方式在XHR中通過超時來實現取消正在進行中的操作。 ## 4.5.1\. 讓Promise等待指定時間 首先我們來看一下如何在Promise中實現超時。 所謂超時就是要在經過一定時間后進行某些操作,使用?`setTimeout`?的話很好理解。 首先我們來串講一個單純的在Promise中調用?`setTimeout`?的函數。 delayPromise.js ~~~ function delayPromise(ms) { return new Promise(function (resolve) { setTimeout(resolve, ms); }); } ~~~ `delayPromise(ms)`?返回一個在經過了參數指定的毫秒數后進行onFulfilled操作的promise對象,這和直接使用?`setTimeout`?函數比較起來只是編碼上略有不同,如下所示。 ~~~ setTimeout(function () { alert("已經過了100ms!"); }, 100); // == 幾乎同樣的操作 delayPromise(100).then(function () { alert("已經過了100ms!"); }); ~~~ 在這里?**promise對象**?這個概念非常重要,請切記。 ## 4.5.2\. Promise.race中的超時 讓我們回顧一下靜態方法?`Promise.race`?,它的作用是在任何一個promise對象進入到確定(解決)狀態后就繼續進行后續處理,如下面的例子所示。 ~~~ var winnerPromise = new Promise(function (resolve) { setTimeout(function () { console.log('this is winner'); resolve('this is winner'); }, 4); }); var loserPromise = new Promise(function (resolve) { setTimeout(function () { console.log('this is loser'); resolve('this is loser'); }, 1000); }); // 第一個promise變為resolve后程序停止 Promise.race([winnerPromise, loserPromise]).then(function (value) { console.log(value); // => 'this is winner' }); ~~~ 我們可以將剛才的?[delayPromise](http://liubin.github.io/promises-book/#delayPromise.js)?和其它promise對象一起放到?`Promise.race`?中來是實現簡單的超時機制。 simple-timeout-promise.js ~~~ function delayPromise(ms) { return new Promise(function (resolve) { setTimeout(resolve, ms); }); } function timeoutPromise(promise, ms) { var timeout = delayPromise(ms).then(function () { throw new Error('Operation timed out after ' + ms + ' ms'); }); return Promise.race([promise, timeout]); } ~~~ 函數?`timeoutPromise(比較對象promise, ms)`?接收兩個參數,第一個是需要使用超時機制的promise對象,第二個參數是超時時間,它返回一個由?`Promise.race`?創建的相互競爭的promise對象。 之后我們就可以使用?`timeoutPromise`?編寫下面這樣的具有超時機制的代碼了。 ~~~ function delayPromise(ms) { return new Promise(function (resolve) { setTimeout(resolve, ms); }); } function timeoutPromise(promise, ms) { var timeout = delayPromise(ms).then(function () { throw new Error('Operation timed out after ' + ms + ' ms'); }); return Promise.race([promise, timeout]); } // 運行示例 var taskPromise = new Promise(function(resolve){ // 隨便一些什么處理 var delay = Math.random() * 2000; setTimeout(function(){ resolve(delay + "ms"); }, delay); }); timeoutPromise(taskPromise, 1000).then(function(value){ console.log("taskPromise在規定時間內結束 : " + value); }).catch(function(error){ console.log("發生超時", error); }); ~~~ 雖然在發生超時的時候拋出了異常,但是這樣的話我們就不能區分這個異常到底是_普通的錯誤_還是_超時錯誤_了。 為了能區分這個?`Error`?對象的類型,我們再來定義一個`Error`?對象的子類?`TimeoutError`。 ## 4.5.3\. 定制Error對象 `Error`?對象是ECMAScript的內建(build in)對象。 但是由于stack trace等原因我們不能完美的創建一個繼承自?`Error`?的類,不過在這里我們的目的只是為了和Error有所區別,我們將創建一個?`TimeoutError`?類來實現我們的目的。 > 在ECMAScript6中可以使用?`class`?語法來定義類之間的繼承關系。 > ~~~ > class MyError extends Error{ > // 繼承了Error類的對象 > } > ~~~ 為了讓我們的?`TimeoutError`?能支持類似?`error instanceof TimeoutError`?的使用方法,我們還需要進行如下工作。 TimeoutError.js ~~~ function copyOwnFrom(target, source) { Object.getOwnPropertyNames(source).forEach(function (propName) { Object.defineProperty(target, propName, Object.getOwnPropertyDescriptor(source, propName)); }); return target; } function TimeoutError() { var superInstance = Error.apply(null, arguments); copyOwnFrom(this, superInstance); } TimeoutError.prototype = Object.create(Error.prototype); TimeoutError.prototype.constructor = TimeoutError; ~~~ 我們定義了?`TimeoutError`?類和構造函數,這個類繼承了Error的prototype。 它的使用方法和普通的?`Error`?對象一樣,使用?`throw`?語句即可,如下所示。 ~~~ var promise = new Promise(function(){ throw TimeoutError("timeout"); }); promise.catch(function(error){ console.log(error instanceof TimeoutError);// true }); ~~~ 有了這個?`TimeoutError`?對象,我們就能很容易區分捕獲的到底是因為超時而導致的錯誤,還是其他原因導致的Error對象了。 > 本章里介紹的繼承JavaScript內建對象的方法可以參考?[Chapter?28.?Subclassing Built-ins](http://speakingjs.com/es5/ch28.html)?,那里有詳細的說明。此外?[Error - JavaScript | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)?也針對Error對象進行了詳細說明。 ## 4.5.4\. 通過超時取消XHR操作 到這里,我想各位讀者都已經對如何使用Promise來取消一個XHR請求都有一些思路了吧。 取消XHR操作本身的話并不難,只需要調用?`XMLHttpRequest`?對象的?`abort()`?方法就可以了。 為了能在外部調用?`abort()`?方法,我們先對之前本節出現的?[`getURL`](http://liubin.github.io/promises-book/#xhr-promise.js)?進行簡單的擴展,`cancelableXHR`?方法除了返回一個包裝了XHR的promise對象之外,還返回了一個用于取消該XHR請求的`abort`方法。 delay-race-cancel.js ~~~ function cancelableXHR(URL) { var req = new XMLHttpRequest(); var promise = new Promise(function (resolve, reject) { req.open('GET', URL, true); req.onload = function () { if (req.status === 200) { resolve(req.responseText); } else { reject(new Error(req.statusText)); } }; req.onerror = function () { reject(new Error(req.statusText)); }; req.onabort = function () { reject(new Error('abort this request')); }; req.send(); }); var abort = function () { // 如果request還沒有結束的話就執行abort // https://developer.mozilla.org/en/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest if (req.readyState !== XMLHttpRequest.UNSENT) { req.abort(); } }; return { promise: promise, abort: abort }; } ~~~ 在這些問題都明了之后,剩下只需要進行Promise處理的流程進行編碼即可。大體的流程就像下面這樣。 1. 通過?`cancelableXHR`?方法取得包裝了XHR的promise對象和取消該XHR請求的方法 2. 在?`timeoutPromise`?方法中通過?`Promise.race`?讓XHR的包裝promise和超時用promise進行競爭。 * XHR在超時前返回結果的話 1. 和正常的promise一樣,通過?`then`?返回請求結果 * 發生超時的時候 1. 拋出?`throw TimeoutError`?異常并被?`catch` 2. catch的錯誤對象如果是?`TimeoutError`?類型的話,則調用?`abort`?方法取消XHR請求 將上面的步驟總結一下的話,代碼如下所示。 delay-race-cancel-play.js ~~~ function copyOwnFrom(target, source) { Object.getOwnPropertyNames(source).forEach(function (propName) { Object.defineProperty(target, propName, Object.getOwnPropertyDescriptor(source, propName)); }); return target; } function TimeoutError() { var superInstance = Error.apply(null, arguments); copyOwnFrom(this, superInstance); } TimeoutError.prototype = Object.create(Error.prototype); TimeoutError.prototype.constructor = TimeoutError; function delayPromise(ms) { return new Promise(function (resolve) { setTimeout(resolve, ms); }); } function timeoutPromise(promise, ms) { var timeout = delayPromise(ms).then(function () { return Promise.reject(new TimeoutError('Operation timed out after ' + ms + ' ms')); }); return Promise.race([promise, timeout]); } function cancelableXHR(URL) { var req = new XMLHttpRequest(); var promise = new Promise(function (resolve, reject) { req.open('GET', URL, true); req.onload = function () { if (req.status === 200) { resolve(req.responseText); } else { reject(new Error(req.statusText)); } }; req.onerror = function () { reject(new Error(req.statusText)); }; req.onabort = function () { reject(new Error('abort this request')); }; req.send(); }); var abort = function () { // 如果request還沒有結束的話就執行abort // https://developer.mozilla.org/en/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest if (req.readyState !== XMLHttpRequest.UNSENT) { req.abort(); } }; return { promise: promise, abort: abort }; } var object = cancelableXHR('http://httpbin.org/get'); // main timeoutPromise(object.promise, 1000).then(function (contents) { console.log('Contents', contents); }).catch(function (error) { if (error instanceof TimeoutError) { object.abort(); return console.log(error); } console.log('XHR Error :', error); }); ~~~ 上面的代碼就通過在一定的時間內變為解決狀態的promise對象實現了超時處理。 > 通常進行開發的情況下,由于這些邏輯會頻繁使用,因此將這些代碼分割保存在不同的文件應該是一個不錯的選擇。 ## 4.5.5\. promise和操作方法 在前面的?[`cancelableXHR`](http://liubin.github.io/promises-book/#delay-race-cancel.js)?中,promise對象及其操作方法都是在一個對象中返回的,看起來稍微有些不太好理解。 從代碼組織的角度來說一個函數只返回一個值(promise對象)是一個非常好的習慣,但是由于在外面不能訪問?`cancelableXHR`?方法中創建的?`req`?變量,所以我們需要編寫一個專門的函數(上面的例子中的`abort`)來對這些內部對象進行處理。 當然也可以考慮到對返回的promise對象進行擴展,使其支持`abort`方法,但是由于promise對象是對值進行抽象化的對象,如果不加限制的增加操作用的方法的話,會使整體變得非常復雜。 大家都知道一個函數做太多的工作都不認為是一個好的習慣,因此我們不會讓一個函數完成所有功能,也許像下面這樣對函數進行分割是一個不錯的選擇。 * 返回包含XHR的promise對象 * 接收promise對象作為參數并取消該對象中的XHR請求 將這些處理整理為一個模塊的話,以后擴展起來也方便,一個函數所做的工作也會比較精煉,代碼也會更容易閱讀和維護。 我們有很多方法來創建一個模塊(AMD,CommonJS,ES6 module etc..),在這里,我們將會把前面的?`cancelableXHR`?整理為一個Node.js的模塊使用。 cancelableXHR.js ~~~ "use strict"; var requestMap = {}; function createXHRPromise(URL) { var req = new XMLHttpRequest(); var promise = new Promise(function (resolve, reject) { req.open('GET', URL, true); req.onreadystatechange = function () { if (req.readyState === XMLHttpRequest.DONE) { delete requestMap[URL]; } }; req.onload = function () { if (req.status === 200) { resolve(req.responseText); } else { reject(new Error(req.statusText)); } }; req.onerror = function () { reject(new Error(req.statusText)); }; req.onabort = function () { reject(new Error('abort this req')); }; req.send(); }); requestMap[URL] = { promise: promise, request: req }; return promise; } function abortPromise(promise) { if (typeof promise === "undefined") { return; } var request; Object.keys(requestMap).some(function (URL) { if (requestMap[URL].promise === promise) { request = requestMap[URL].request; return true; } }); if (request != null && request.readyState !== XMLHttpRequest.UNSENT) { request.abort(); } } module.exports.createXHRPromise = createXHRPromise; module.exports.abortPromise = abortPromise; ~~~ 使用方法也非常簡單,我們通過?`createXHRPromise`?方法得到XHR的promise對象,當想對這個XHR進行`abort`操作的時候,將這個promise對象傳遞給?`abortPromise(promise)`?方法就可以了。 ~~~ var cancelableXHR = require("./cancelableXHR"); var xhrPromise = cancelableXHR.createXHRPromise('http://httpbin.org/get');//創建包裝了XHR的promise對象 xhrPromise.catch(function (error) { // 調用 abort 拋出的錯誤 }); cancelableXHR.abortPromise(xhrPromise);//取消在1中創建的promise對象的請求操作 ~~~ ## 4.5.6\. 總結 在這里我們學到了如下內容。 * 經過一定時間后變為解決狀態的delayPromise * 基于delayPromise和Promise.race的超時實現方式 * 取消XHR promise請求 * 通過模塊化實現promise對象和操作的分離 Promise能非常靈活的進行處理流程的控制,為了充分發揮它的能力,我們需要注意不要將一個函數寫的過于龐大冗長,而是應該將其分割成更小更簡單的處理,并對之前JavaScript中提到的機制進行更深入的了解。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看