由使用request-promise-native想到的非同步處理方法 問題場景 因為js語言的特性,使用node開發程式的時候經常會遇到非同步處理的問題。對於之前專長App開發的我來說,會糾結node中實現客戶端API請求的“最佳實踐”。下麵以OAuth2.0為場景,需要處理的流程: 處理過程 一開始 ...
由使用request-promise-native想到的非同步處理方法
問題場景
因為js語言的特性,使用node開發程式的時候經常會遇到非同步處理的問題。對於之前專長App開發的我來說,會糾結node中實現客戶端API請求的“最佳實踐”。下麵以OAuth2.0為場景,需要處理的流程:
- 獲取access token
- 使用獲取到的token,發起API請求
- 處理API數據
處理過程
一開始,我們使用了閉包嵌套閉包的方式實現,形如:
request(options, (res, error)=>{ //handle res and error request(options2, (res2, error2)=>{ //handle res2 and error2 }) })
我們可以允許函數的非同步執行,但大多數人在思考問題的時候,尤其在解決如上的場景時,還是希望能採用線性地處理方式。於是,我們使用request-promise-native
,配合aync/await,類似:
1 (async ()=> { 2 let access = await requestpromise(authoptions).then((value)=>{ 3 return value; 4 }).catch((error)=>{ 5 return error; 6 }); 7 console.log('access', access); 8 })();
使用async/await的時候,需要知道:
- await不能單獨使用,其所在的上下文之前必須有async
- await 作用的對象是Promise對象
可以猜想 request-promise-native
必定是對request進行了Promise化,從源代碼中可以看到(雖然我沒看懂,應該是使用了通用的方法來創建Promise):
// Exposing the Promise capabilities var thenExposed = false; for ( var i = 0; i < options.expose.length; i+=1 ) { var method = options.expose[i]; plumbing[ method === 'promise' ? 'exposePromise' : 'exposePromiseMethod' ]( options.request.Request.prototype, null, '_rp_promise', method ); if (method === 'then') { thenExposed = true; } } if (!thenExposed) { throw new Error('Please expose "then"'); }
既然如此,我們可以構造Promise,交給await。下麵就把request
包裹成一個Promise:
1 //token.js 2 module.exports.getAccessToken = async (options) => { 3 return new Promise(function (resolve, reject) { 4 request(options, function (error, res, body) { 5 if (!error && res.statusCode == 200) { 6 resolve(body); 7 } else { 8 if(error){ 9 reject(error); 10 }else{ 11 reject(body); 12 } 13 } 14 }); 15 }); 16 }; 17 //app.js 18 (async ()=> { 19 let access = await token.getAccessToken(authoptions).then((value)=>{ 20 //handle value if requires 21 return value; 22 }).catch((error)=>{ 23 return error; 24 }); 25 console.log('access', access); 26 //use token to send the request 27 })();
API成功返回的結果我們往往需要按需處理,這一步放在then函數中進行。因為Promise調用then仍然是Promise,因此這裡鏈式調用的then和catch。
進一步地,我們嘗試使用內置模塊 util
對函數進行promise化,形如:
//token.js const request = require('request'); const {promisify} = require('util'); const requestPromise = promisify(request); module.exports.getAccessToken = async (options) => { return requestPromise(options); }; //app.js (async ()=> { let access = await token.getAccessToken(authoptions).then((value)=>{ //handle value if requires return value; }).catch((error)=>{ return error; }); console.log('access', access); //use token to send the request })();
說了這麼多,對我而言,目前最大的收穫就是理解瞭如何使用Promise/async/await,把非同步函數順序執行:把帶有閉包的函數包裹進Promise,然後使用async/await執行該Promise。
好了,以上是我解決此類問題的思路。我相信必然還有其他優雅的解決方式,甚至是最佳實踐。今天,藉此機會,拋磚引玉,希望大家能夠不吝賜教。
Promise 內容複習
最後,容我溫習一下Promise相關的內容,有片面的地方請大家指正。
Promise對象:
The Promise object represents the eventual completion (or failure) of an asynchronous operation, and its resulting value.
Promise有三種狀態: 初始狀態,執行成功,執行出錯。 then()表示promise執行後的進一步處理,它可以帶兩個callback參數:第一個用於promise成功運行後執行,第二個表示promise運行失敗後執行。catch()表示promise運行失敗後所執行的工作。catch()可以理解為語法糖,當then()的第二個callback參數省略的時候,意味著需要調用catch(因為未處理的失敗的promise在將來某個node版本會導致程式退出)。需要註意的是,then()/catch()方法也是返回Promise,因此可以鏈式調用。
參考
Promise-MDN web docs
用圖表和實例解釋 Await 和 Async