Node.js 8 於上個月月底正式發佈,帶來了不少新特性。其中比較值得注意的,便有 util.promisify()
這個方法。javascript
若是你已經很熟悉 Promise,請繼續往下看。若是你還不熟悉 Promise,能夠先跳過去看下下章:Promise 介紹。html
util.promisify()
雖然 Promise 已經普及,可是 Node.js 裏仍然有大量依賴回調的異步函數,若是咱們把每一個函數都封裝一遍,那真是齁麻煩齁麻煩的,比齁還麻煩。java
因此 Node.js 8 就提供了 util.promisify()
這個方法,方便咱們把原來的異步回調方法改爲支持 Promise 的方法,接下來,想繼續 .then().then().then()
搞隊列,仍是 await 就看實際須要了。node
咱們看下範例,讓讀取目錄文件狀態的 fs.stat
支持 Promise:jquery
const util = require('util'); const fs = require('fs'); const stat = util.promisify(fs.stat); stat('.') .then((stats) => { // Do something with `stats` }) .catch((error) => { // Handle the error. });
怎麼樣,很簡單吧?按照文檔的說法,只要符合 Node.js 的回調風格,全部函數均可以這樣轉換。也就是說,只要知足下面兩個條件,不管是否是原生方法,均可以:git
最後一個參數是回調函數github
回調函數的參數爲 (err, result)
,前面是可能的錯誤,後面是正常的結果小程序
一樣是上面的例子,若是想要結合 Await/Async,能夠這樣使用:segmentfault
const util = require('util'); const fs = require('fs'); const stat = util.promisify(fs.stat); async function readStats(dir) { try { let stats = await stat(dir); // Do something with `stats` } catch (err) { // Handle the error. console.log(err); } } readStats('.');
那若是現有的使用回調的函數不符合這個風格,還能用 util.promisify()
麼?答案也是確定的。咱們只要給函數增長一個屬性 util.promisify.custom
,指定一個函數做爲 Promise 化處理函數,便可。請看下面的代碼:api
const util = require('util'); // 這就是要處理的使用回調的函數 function doSomething(foo, callback) { // ... } // 給它增長一個方法,用來在 Promise 化時調用 doSomething[util.promisify.custom] = function(foo) { // 自定義生成 Promise 的邏輯 return getPromiseSomehow(); }; const promisified = util.promisify(doSomething); console.log(promisified === doSomething[util.promisify.custom]); // prints 'true'
如此一來,任什麼時候候咱們對目標函數 doSomething 進行 Promise 化處理,都會獲得以前定義的函數。運行它,就會按照咱們設計的特定邏輯返回 Promise 實例。
咱們就能夠升級之前全部的異步回調函數了。
由於種種歷史緣由,JS 當中有大量異步函數。這些異步函數,大多要依賴回調進行處理(這裏我以爲把事件偵聽算做回調也是合理的),可是回調嵌套層次一多,就會造成所謂的「回調陷阱」,讓開發者苦不堪言。
爲了解決這個問題,開發社區通過摸索,總結出來一套名爲 Promise/A+ 的解決方案。大致上來講,這套方案經過使用 「Promise 回調實例」包裹原先的回調函數,能夠將原先複雜的嵌套展開、鋪平,從而下降開發和維護的難度和成本。
new Promise( (resolve, reject) => { // 構建一個 Promise 實例 someAsyncFunction( (err, result) => { // 調用原來的異步函數 if (err) { // 發生錯誤,進入錯誤處理模式 return reject(err); } resolve(result); // 一切正常,進入隊列的下一環節 }); }) .then( result => { // 下一環節 return doSomething(result); }) .then( result2 => { // 又下一環節 return doSomething2(result2); }) ... // 各類中間環節 .catch( err => { // 錯誤處理 console.log(err); });
ES2015(ES6)裏包含了 Promise 標準,現在已經在大部分運行時裏實裝,咱們能夠放心大膽的使用它。並且,因爲 Promise 不須要新的語法元素,因此即便在不支持原生 Promise 的環境裏也可使用類庫,好比 Q 或者 Bluebird,甚至 jQuery。
在小程序裏也有效喲!
ES2017 增長了 Await/Async 語法,但請注意,Await
後面必須跟 Promise 實例才能實現異步。因此,你們仍是把 Promise 的概念學好吧!
function resolveAfter2Seconds(x) { return new Promise(resolve => { setTimeout(() => { resolve(x); }, 2000); }); } async function f1() { var x = await resolveAfter2Seconds(10); console.log(x); // 10 } f1();
例子來源於 MDN。
若是你想進一步學習使用 Promise,強烈推薦個人此次分享:Promise 的 N 種用法。能夠幫助你一站式的學會使用 Promise。
PS2:剛纔看到 Node.js 已經發布 8.1 了,真快呀……新版本的 Changelog 在這裏,已修復爲主。
同步發於 個人博客