JavaScript Promise 用法
同步編程通常來說易于調(diào)試和維護(hù),然而,異步編程通常能獲得更好的性能和更大的靈活性。異步的最大特點(diǎn)是無需等待?!癙romises”漸漸成為JavaScript里最重要的一部分,大量的新API都開始promise原理實(shí)現(xiàn)。下面讓我們看一下什么是promise,以及它的API和用法!
Promises現(xiàn)狀
XMLHttpRequest API是異步的,但它沒有使用promise API。但有很多原生的 javascript API 使用了promise:
*Battery API
*fetch API (XHR的替代品)
*ServiceWorker API
Promises將來只會(huì)變得越來越流行、普遍,非常重要,所有的前端開發(fā)人員都將用到它。另一個(gè)值得注意的是,Node.js是基于Promises的平臺(tái)(很顯然,Promise是它的一個(gè)核心特征)。
Promises的用法比你想象的要簡(jiǎn)單——如果你以前喜歡使用setTimeout來控制異步任務(wù)的話!
Promise基本用法
new Promise()構(gòu)造器可以用在傳統(tǒng)的異步任務(wù)中,就像以前 setTimeout 和 XMLHttpRequest 的用法一樣。一個(gè)新的 Promise 使用 new 關(guān)鍵字生成,同時(shí),這個(gè) Promises 提供了 resolve 和 reject 函數(shù)讓我們執(zhí)行回調(diào)操作:
var p = new Promise(function(resolve, reject) { // Do an async task async task and then... if(/* good condition */) { resolve('Success!'); } else { reject('Failure!'); } }); p.then(function() { /* do something with the result */ }).catch(function() { /* error */ })
程序員可以手動(dòng)的在回調(diào)函數(shù)內(nèi)部根據(jù)執(zhí)行情況調(diào)用 resolve 和 reject 函數(shù)。下面是一個(gè)比較具有現(xiàn)實(shí)意義的例子,它將一個(gè) XMLHttpRequest 調(diào)用轉(zhuǎn)換為 基于 Promises 的任務(wù):
// From Jake Archibald's Promises and Back: // http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promisifying-xmlhttprequest function get(url) { // Return a new promise. return new Promise(function(resolve, reject) { // Do the usual XHR stuff 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("Network Error")); }; // Make the request req.send(); }); } // Use it! get('story.json').then(function(response) { console.log("Success!", response); }, function(error) { console.error("Failed!", error); });
Promise.resolve() 和 Promise.reject() 可以直接被調(diào)用。有時(shí)候,當(dāng)判斷出 promise 并不需要真正執(zhí)行時(shí),我們并不需要 使用 new 創(chuàng)建 Promise 對(duì)象,而是可以直接調(diào)用 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); }); }
因?yàn)?promise 肯定會(huì)返回,所以,我們可以使用 then 和 catch 方法處理返回值!
then 方法
所有的 promise 對(duì)象實(shí)例里都有一個(gè) then 方法,它是用來跟這個(gè) promise 進(jìn)行交互的。首先,then 方法會(huì)缺省調(diào)用 resolve() 函數(shù):
new Promise(function(resolve, reject) { // A mock async action using setTimeout setTimeout(function() { resolve(10); }, 3000); }) .then(function(result) { console.log(result); }); // From the console: // 10
then 回調(diào)動(dòng)作的觸發(fā)時(shí)機(jī)是 promise 被執(zhí)行完。我們還可以串聯(lián) then 方法執(zhí)行回調(diào)操作:
new Promise(function(resolve, reject) { // A mock async action using 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);}); // From the console: // first then: 10 // second then: 20 // last then: 40
你會(huì)發(fā)現(xiàn),每次 then 調(diào)用都會(huì)以之前的 then 調(diào)用的返回值為參數(shù)。
如果一個(gè) promise 已經(jīng)執(zhí)行完成,單 then 被再次調(diào)用時(shí),回調(diào)動(dòng)作將會(huì)被再次執(zhí)行。而如果這個(gè) promise 里執(zhí)行的是reject 回調(diào)函數(shù),這是再調(diào)用 then 方法,回調(diào)函數(shù)將不會(huì)被執(zhí)行。
catch 方法
catch 當(dāng)一個(gè) promise 被拒絕(reject)時(shí),catch 方法會(huì)被執(zhí)行:
new Promise(function(resolve, reject) { // A mock async action using setTimeout setTimeout(function() { reject('Done!'); }, 3000); }) .then(function(e) { console.log('done', e); }) .catch(function(e) { console.log('catch: ', e); }); // From the console: // 'catch: Done!'
通常我們?cè)?reject 方法里處理執(zhí)行失敗的結(jié)果,而在catch 里執(zhí)行異常結(jié)果:
reject(Error('Data could not be found'));
Promise.all 方法
在我們的異步調(diào)用時(shí)經(jīng)常有這樣一種場(chǎng)景:我們需要同時(shí)調(diào)用多個(gè)異步操作,但希望只有等所有的操作都完成后,我們才去執(zhí)行響應(yīng)操作——這就是 Promise.all 的作用。 Promise.all 方法可以接收多個(gè) promise 作為參數(shù),以數(shù)組的形式,當(dāng)這些 promise 都成功執(zhí)行完成后才調(diào)用回調(diào)函數(shù)。
Promise.all([promise1, promise2]).then(function(results) { // Both promises resolved }) .catch(function(error) { // One or more promises was rejected });
一個(gè)很好的能演示 Promise.all 用法的例子是,執(zhí)行多個(gè) AJAX 操作(通過 fetch) 調(diào)用:
var request1 = fetch('/users.json'); var request2 = fetch('/articles.json'); Promise.all([request1, request2]).then(function(results) { // Both promises done! });
我們還可將fetch和電池狀態(tài)API混合一起執(zhí)行,因?yàn)樗鼈兎祷氐亩际?promise:
Promise.all([fetch('/users.json'), navigator.getBattery()]).then(function(results) { // Both promises done! });
一旦 promise 里調(diào)用了reject函數(shù),也就是執(zhí)行被拒絕了,沒有能夠正常完成,情況會(huì)有些復(fù)雜。一旦 promise 被拒絕,catch 方法會(huì)捕捉到首個(gè)被執(zhí)行的reject函數(shù):
var req1 = new Promise(function(resolve, reject) { // A mock async action using setTimeout setTimeout(function() { resolve('First!'); }, 4000); }); var req2 = new Promise(function(resolve, reject) { // A mock async action using 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!
Promise.all 是非常重要的接口,將會(huì)在很多新誕生的 promise API中扮演重要的作用。
Promise.race
Promise.race 是一個(gè)有趣的函數(shù)——它不是等待所有的 promise 被resolve 或 reject,而是在所有的 promise 中只要有一個(gè)執(zhí)行結(jié)束,它就會(huì)觸發(fā):
var req1 = new Promise(function(resolve, reject) { // A mock async action using setTimeout setTimeout(function() { resolve('First!'); }, 8000); }); var req2 = new Promise(function(resolve, reject) { // A mock async action using 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!
一個(gè)有用的場(chǎng)景是,從多個(gè)鏡像服務(wù)器下載資源,一旦有一個(gè)返回,其它的返回也就不用處理了。
學(xué)會(huì)使用 Promises
Promises在過去幾年是一個(gè)非常火爆的話題,它甚至從JavaScript里抽離出來變成了一個(gè)語言架構(gòu)。相信很快我們將見到有愈來愈多的JavaScript API將使用以promise為基礎(chǔ)的模式。
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
學(xué)習(xí)javascript的閉包,原型,和匿名函數(shù)之旅
Javascript中有幾個(gè)非常重要的語言特性——對(duì)象、原型繼承、閉包。其中閉包 對(duì)于那些使用傳統(tǒng)靜態(tài)語言C/C++的程序員來說是一個(gè)新的語言特性,本文給大家介紹js的閉包,原型,和匿名函數(shù)之旅,感興趣的朋友一起學(xué)習(xí)吧2015-10-10淺析Javascript中“==”與“===”的區(qū)別
這篇文章主要介紹了淺析Javascript中“==”與“===”的區(qū)別,非常的全面,這里推薦給小伙伴們2014-12-12JS模擬實(shí)現(xiàn)京東快遞單號(hào)查詢
這篇文章主要為大家詳細(xì)介紹了JS模擬實(shí)現(xiàn)京東快遞單號(hào)查詢,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-11-11有關(guān)suggest快速刪除后仍然出現(xiàn)下拉列表的bug問題
寫suggest的時(shí)候,有時(shí)我們快速刪除輸入框的文字后,但是suggest下拉列表還有出現(xiàn),導(dǎo)致的原因是因?yàn)閍jax異步請(qǐng)求造成的,下面通過本文給大家分享下解決方法,感興趣的朋友一起看看2016-12-12小程序如何實(shí)現(xiàn)中間帶加號(hào)的tabbar
自定義tabBar可以讓開發(fā)者更加靈活地設(shè)置tabBar樣式,以滿足更多個(gè)性化的場(chǎng)景,下面這篇文章主要給大家介紹了關(guān)于小程序如何實(shí)現(xiàn)中間帶加號(hào)tabbar的相關(guān)資料,需要的朋友可以參考下2022-04-04