Promise API 簡介

Promise API 簡介

譯者注: 處處是回調函數,代碼很是臃腫難看, Promise 主要用來解決這種編程方式, 將某些代碼封裝於內部。javascript

Promise 直譯爲「承諾」,但通常直接稱爲 Promise;html

代碼的可讀性很是重要,由於開發人員支出通常比計算機硬件的支出要大不少倍。前端

雖然同步代碼更容易跟蹤和調試, 但異步方式卻具備更好的性能與靈活性. 怎樣在同一時刻發起多個請求, 而後分別處理響應結果? Promise 現已成爲 JavaScript 中很是重要的一個組成部分, 不少新的API都以 promise 的方式來實現。下面簡要介紹 promise, 以及相應的 API 和使用示例!html5

Promises 周邊

XMLHttpRequest 是異步API, 但不算 Promise 方式。當前使用 Promise 的原生 api 包括:java

Promise 會愈來愈流行,因此前端開發須要快速掌握它們。固然, Node.js 是另外一個使用 Promise 的平臺(顯然, Promise 在Node中是一個核心特性)。es6

測試 promises 可能比你想象的還要容易, 由於 setTimeout 能夠用來看成異步「任務」!編程

Promise 基本用法

直接使用 new Promise() 構造函數的方式, 應該只用來處理遺留的異步任務編程, 例如 setTimeout 或者 XMLHttpRequest。 經過 new 關鍵字建立一個新的 Promise 對象, 該對象有 resolve(搞定!) 和 reject(拒絕!) 兩個回調函數:json

var p = new Promise(function(resolve, reject) {
	// ... ... 
	// 此處,能夠執行某些異步任務,而後...
	// 在回調中,或者任何地方執行 resolve/reject

	if(/* good condition */) {
		resolve('傳入成果結果信息,如 data');
	}
	else {
		reject('失敗:緣由...!');
	}
});

p.then(function(data) { 
	/* do something with the result */
}).catch(function(err) {
	/* error :( */
});

通常是由開發人員根據異步任務執行的結果,來手動調用 resolve 或者 reject. 一個典型的例子是將 XMLHttpRequest 轉換爲基於Promise的任務:api

// 本段示例代碼來源於 Jake Archibald's Promises and Back:
// http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promisifying-xmlhttprequest

function get(url) {
  // 返回一個 promise 對象.
  return new Promise(function(resolve, reject) {
    // 執行常規的 XHR 請求
    var req = new XMLHttpRequest();
    req.open('GET', url);

    req.onload = function() {
      // This is called even on 404 etc
      // so check the status
      if (req.status == 200) {
	// Resolve the promise with the response text
	resolve(req.response);
      }
      else {
	// Otherwise reject with the status text
	// which will hopefully be a meaningful error
	reject(Error(req.statusText));
      }
    };

    // Handle network errors
    req.onerror = function() {
      reject(Error("網絡出錯"));
    };

    // Make the request
    req.send();
  });
};

// 使用!
get('story.json').then(function(response) {
  console.log("Success!", response);
}, function(error) {
  console.error("Failed!", error);
});

有時候在 promise 方法體中不須要執行異步任務 —— 固然,在有可能會執行異步任務的狀況下, 返回 promise 將是最好的方式, 這樣只須要給定結果處理函數就行。在這種狀況下, 不須要使用 new 關鍵字, 直接返回 Promise.resolve() 或者 Promise.reject()便可。例如:數組

var userCache = {};

function getUserDetail(username) {
  // In both cases, cached or not, a promise will be returned

  if (userCache[username]) {
	// Return a promise without the "new" keyword
    return Promise.resolve(userCache[username]);
  }

  // Use the fetch API to get the information
  // fetch returns a promise
  return fetch('users/' + username + '.json')
    .then(function(result) {
      userCache[username] = result;
      return result;
    })
    .catch(function() {
      throw new Error('Could not find user: ' + username);
    });
};

由於老是會返回 promise, 因此只須要經過 thencatch 方法處理結果便可!

then

每一個 promise 實例都有 then 方法, 用來處理執行結果。 第一個 then 方法回調的參數, 就是 resolve() 傳入的那個值:

new Promise(function(resolve, reject) {
	// 經過 setTimeout 模擬異步任務
	setTimeout(function() { resolve(10); }, 3000);
})
.then(function(result) {
	console.log(result);
});

// console 輸出的結果:
// 10

then 回調由 promise 的 resolved 觸發。你也可使用鏈式的 then` 回調方法:

new Promise(function(resolve, reject) { 
	// 經過 setTimeout 模擬異步任務
	setTimeout(function() { resolve(10); }, 3000);
})
.then(function(num) { console.log('first then: ', num); return num * 2; })
.then(function(num) { console.log('second then: ', num); return num * 2; })
.then(function(num) { console.log('last then: ', num);});

// console 輸出的結果:
// first then:  10
// second then:  20
// last then:  40

每一個 then 收到的結果都是以前那個 then 返回的值。

若是 promise 已經 resolved, 但以後才調用 then 方法, 則當即觸發回調。若是promise被拒絕以後才調用 then, 則回調函數不會被觸發。

catch

當 promise 被拒絕時, catch 回調就會被執行:

new Promise(function(resolve, reject) {
	// 經過 setTimeout 模擬異步任務
	setTimeout(function() { reject('Done!'); }, 3000);
})
.then(function(e) { console.log('done', e); })
.catch(function(e) { console.log('catch: ', e); });

// console 輸出的結果:
// 'catch: Done!'

傳入 reject 方法的參數由你本身決定。通常來講是傳入一個 Error 對象:

reject(Error('Data could not be found'));

Promise.all

想一想JavaScript加載器的情形: 有時候會觸發多個異步交互, 但只在全部請求完成以後纔會作出響應。—— 這種狀況可使用 Promise.all 來處理。Promise.all 方法接受一個 promise 數組, 在全部 promises 都搞定以後, 觸發一個回調:

Promise.all([promise1, promise2]).then(function(results) {
	// Both promises resolved
})
.catch(function(error) {
	// One or more promises was rejected
});

Promise.all 的最佳示例是經過fetch同時發起多個 AJAX請求時:

var request1 = fetch('/users.json');
var request2 = fetch('/articles.json');

Promise.all([request1, request2]).then(function(results) {
	// Both promises done!
});

你也能夠組合使用 fetch 和 Battery 之類的 API ,由於他們都返回 promises:

Promise.all([fetch('/users.json'), navigator.getBattery()]).then(function(results) {
	// Both promises done!
});

固然, 處理拒絕的狀況比較複雜。若是某個 promise 被拒絕, 則 catch 將會被第一個拒絕(rejection)所觸發:

var req1 = new Promise(function(resolve, reject) { 
	// 經過 setTimeout 模擬異步任務
	setTimeout(function() { resolve('First!'); }, 4000);
});
var req2 = new Promise(function(resolve, reject) { 
	// 經過 setTimeout 模擬異步任務
	setTimeout(function() { reject('Second!'); }, 3000);
});
Promise.all([req1, req2]).then(function(results) {
	console.log('Then: ', one);
}).catch(function(err) {
	console.log('Catch: ', err);
});

// From the console:
// Catch: Second!

隨着愈來愈多的 API 支持 promise, Promise.all 將會變得超級有用。

Promise.race

Promise.race 是一個有趣的函數. 與 Promise.all 相反, 只要某個 priomise 被 resolved 或者 rejected, 就會觸發 Promise.race:

var req1 = new Promise(function(resolve, reject) { 
	// 經過 setTimeout 模擬異步任務
	setTimeout(function() { resolve('First!'); }, 8000);
});
var req2 = new Promise(function(resolve, reject) { 
	// 經過 setTimeout 模擬異步任務
	setTimeout(function() { resolve('Second!'); }, 3000);
});
Promise.race([req1, req2]).then(function(one) {
	console.log('Then: ', one);
}).catch(function(one, two) {
	console.log('Catch: ', one);
});

// From the console:
// Then: Second!

一個案例是請求的資源有 主站資源和備用資源(以防某個不可用)。

改變習慣, 使用 Promise

在過去幾年中 Promise 一直是個熱門話題(若是你是 Dojo Toolkit 用戶,那麼就是已經有10年了), 已經從一個JavaScript框架變成了語言的一個主要成分. 很快你就會看到大多數新的 JavaScript api 都會基於 Promise 的方式來實現……

... 固然這是一件好事! 開發人員可以避開回調的地獄, 異步交互也能夠像其餘變量同樣傳遞. Promise 還須要一段時間來普及, 如今是時候去學習他們了!

本文轉載自:衆成翻譯 譯者:鐵胖子 連接:http://www.zcfy.cc/article/351 原文:https://davidwalsh.name/promises

相關文章
相關標籤/搜索