使用 Promise 封裝 FileReader

我近期在 SF 作了一場關於 Promise 的專題分享,作的很用心,內容也很豐富,基本能夠一站式解決全部關於 Promise 的問題。歡迎你們前來圍觀:javascript

Promise 的 N 種用法html


Promise 在處理異步的時候是個很好的選擇,能夠減小嵌套層次,讓代碼更好讀,邏輯更清晰。ES6 將其加入規範,jQuery 3.0 也修改實現向規範靠攏(3.0 發佈公告)。一些新增元素好比 .fetch() 原生就 「thenable」,不過大多數以往的 API 還要依賴回調,這個時候,咱們只要將它們從新封裝,就能避開嵌套陷阱,享受 Promise 帶來的愉悅體驗。java

Promise 通常用法

先來看下 Promise 的通常用法。jquery

// 聲明 Promise 對象
var p = new Promise(function (resolve, reject) {
  // 無論啥時候,該執行then了,就調用 resolve
  setTimeout(function () { 
    resolve(1);
  }, 5000);

  // 或者無論啥問題,就調用 reject
  if (somethingWrong) {
    reject('2');
  }      
});
    
// 使用 Promise 對象
p.then(function (num) {
  // 對應上面的 resolve
  console.log(num); // 1
}, function (num) {
  // 對應上面的 reject
  console.log(num); // 2
});

Promise 的驅動模型並不複雜:任何操做,假定它只有兩個結果,成功或者失敗。那麼只須要在合適的時間調用合適的程序,進入合適的後續步驟便可。.then() 顧名思義,就是下一步的意思,當前面的 Promise 有告終果——即調用 resolve 或者 reject——以後,就啓動對應的處理函數。git

Promise 實例建立後就會開始執行,斷定結果須要咱們本身來,好比加載成功,或者知足某個條件,等等。經過串聯 .then() 則能夠完成一系列操做。每次調用 .then() 都會建立一個新的 Promise 實例,它會靜靜等待前面的實例狀態改變後再開始執行。es6

封裝 FileReader

接下來開始封裝。思路很簡單,FileReader 除了提供各類 read 方法,還有幾個事件鉤子,其中 onerroronload 很明顯能夠做爲判斷任務是否完成的依據。加載成功的話,就須要用到文件內容,因此將文件或文件內容傳遞到下一步也十分必要。github

最後完成的代碼以下:segmentfault

function reader (file, options) {
  options = options || {};
  return new Promise(function (resolve, reject) {
    let reader = new FileReader();

    reader.onload = function () {
      resolve(reader);
    };
    reader.onerror = reject;

    if (options.accept && !new RegExp(options.accept).test(file.type)) {
      reject({
        code: 1,
        msg: 'wrong file type'
      });
    }

    if (!file.type || /^text\//i.test(file.type)) {
      reader.readAsText(file);
    } else {
      reader.readAsDataURL(file);
    }
  });
}

爲了能真正派上用場,裏面還有一些驗證文件類型的操做,不過跟本文主旨無關,略過不表。這段代碼的核心是建立一個 Promise 對象,等待 FileReader 讀取完成後調用 resolve 方法,或者出現問題時調用 reject 方法。promise

Github Gist 裏也放了一份。babel

使用剛纔封裝好的函數

接下來就能夠在項目中使用了:

reader(file)
  .then(function (reader) {
    console.log(reader.result);
  })
  .catch(function (error) {
    console.log(error);
  });

.then() 支持兩個參數,第一個在 Promise 成功時啓動,第二個天然在失敗時啓動。用 .catch() 能夠實現一樣地效果。Promise 的好處除了可讀性更佳之外,返回的 Promise 對象還能夠任意傳遞,繼續進行鏈式調用,有很大想象空間。

繼續 .then()

因而咱們不妨串聯更多操做(原本想寫個斷點續傳的,回頭再說吧):

reader(file)
  .then(function (reader) {
    return new Promise(function (resolve, reject) {
      // 就隨便暫停個5秒吧……
      setTimeout(function () {
        resolve(reader.result); 
      }, 5000);
    });
  })
  .then(function (content) {
    console.log(content);
  });

總結

這實際上是我第一次用 Promise,上次翻譯 jQuery 發佈公告的時候我它也只是只知其一;不知其二,對它的解讀也糊里糊塗。我很喜歡在業餘項目中學習使用新技術,最近開發 Chrome 插件的時候就嘗試了一把,感受不錯。使用過程比我想象的複雜也比我想象的簡單,這套設計很棒,能解決很多實際問題,也給了我很大啓發,未來我應該會把不少地方的實現都作這樣的修改。


參考文獻

除去第一段的各個連接,還有一些文章值得一看。

ECMAScript 6 入門:Promise 對象

相關文章
相關標籤/搜索