util模塊最初的目的是爲內部API提供一些工具支持,然而不少工具函數對於普通的開發者來講也十分有用,所以util模塊將一些方法實現了對外暴露。本文主要探討如下三方面的工具函數:html
針對傳入error-first回調做爲函數的最後一個參數的函數(好比fs.readFile('./filename', (err, data) => {})), util提供了promisify(original)方法用來將這種類型的函數轉換成返回promise的形式。node
以fs.readFile爲例api
const fs = require('fs'); fs.readFile('./h.js', (err, data) => { if (err) { console.error(err); return; } console.log(data.toString()); }) // 使用util.promisify轉換後 const fs = require('fs'); const util = require('util'); const readFilePromise = util.promisify(fs.readFile); readFilePromise('./h.js') .then((data) => { console.log(data.toString()); }, (err) => { console.error(err); });
promisify執行完後返回的是一個新的函數,新的函數的執行結果是一個promise,新函數內部會調用original原有的方法而且會自動追加error-first類型的callback,根據original的執行結果判斷是resolve仍是reject,簡易版本的代碼以下:promise
function promisify(original) { function fn(...args) { const promise = createPromise(); try { original.call(this, ...args, (err, ...values) => { if (err) { promiseReject(promise, err); } else { promiseResolve(promise, values[0]); } }); } catch (err) { promiseReject(promise, err); } return promise; } return fn }
util模塊還提供了promisify的自定義轉換方式(original函數上定義util.promisify.custom屬性),好比經過下面的方式能夠實現禁用文件讀取,util.promisify.custom必須定義在util.promisify調用以前app
const fs = require('fs'); const util = require('util'); fs.readFile[util.promisify.custom] = (fileName) => { return Promise.reject('not allowed'); } const readFilePromise = util.promisify(fs.readFile); readFilePromise('./h.js') .then((data) => { console.log(data.toString()); }, (err) => { console.error(err); // not allowed });
util的callbackify方法與promisify恰好相反,callbackify用於把async(或者返回promise)的函數轉換成聽從error-first回調風格的類型async
const util = require('util'); const fn = () => { return 'fn executed' }; function delay(second, fn) { return new Promise((resolve, reject) => { setTimeout(() => { resolve(fn()); }, second); }); } delay(1000, fn) .then((data) => { console.log(data); // fn executed }); // 使用util.callbackify轉換後 const delayFn = util.callbackify(delay); delayFn(1000, fn, (err, data) => { if (err) { console.error(err); return; } console.log(data); // fn executed });
有一種狀況須要關注,假如promise是reject(null || 0 || false)的話,那麼callback的err判斷爲非,程序會繼續執行console.log(data),這實際上是不正確的。所以callbackify對於這種狀況作了特殊的處理(建立一個error,將原始的信息放在error的reason屬性上,把error傳遞給最終的回調函數)函數
function callbackifyOnRejected(reason, cb) { if (!reason) { const newReason = new ERR_FALSY_VALUE_REJECTION(); newReason.reason = reason; reason = newReason; Error.captureStackTrace(reason, callbackifyOnRejected); } return cb(reason); }
實現的邏輯是調用原始函數original經過then來調用callback方法工具
function callbackify(original) { function callbackified(...args) { const maybeCb = args.pop(); const cb = (...args) => { Reflect.apply(maybeCb, this, args); }; Reflect.apply(original, this, args) .then((ret) => process.nextTick(cb, null, ret), (rej) => process.nextTick(callbackifyOnRejected, rej, cb)); } return callbackified; }
debuglog方法用於根據NODE_DEBUG環境變量來選擇性的輸出debug信息,例以下面的例子ui
//index.js const util = require('util'); const debuglog = util.debuglog('foo-bar'); debuglog('hello from foo [%d]', 123); // 執行index文件 node index.js // 沒有輸出 NODE_DEBUG=foo-bar node index.js //FOO-BAR 18470: hi there, it's foo-bar [2333]
NODE_DEBUG若是但願輸出多個section能夠用逗號作分隔,同時NODE_DEBUG也支持通配符形式(node版本須要10)this
NODE_DEBUG=fs,net,tls // 多個section NODE_DEBUG=foo* // 通配符
上面的debuglog函數執行的時候支持佔位符,其實底層使用的是util.format方法。
util.format用於佔位符替換,不一樣類型的數據採用不一樣的佔位符表示
%s | 字符串 |
---|---|
%d | 整數或浮點數 |
%i | 整數 |
%f | 浮點數 |
%j | JSON |
%o | Object(包括不可枚舉的屬性) |
%O | Object(不包括不可枚舉的屬性) |
%% | 輸出% |
對Object格式化輸出字符串的時候用到的實際上是util.inspect方法,%o與%O的區別僅在於調用inspect方法時傳入配置項有別
%o 傳入util.inspect的配置項是 { showHidden: true, showProxy: true }
util.inspect用於對object作格式化字符串操做,並提供個性化配置項
const util = require('util'); var child = { "name": "child", "age": 18, "friends": [ { "name": "randal", "age" : 19, "friends": [ { "name": "Kate", "age": 18 } ] } ], "motto": "Now this is not the end. It is not even the beginning of the end. But it is, perhaps, the end of the beginning." } console.log(util.inspect(child, { compact: false, depth: null, breakLength: 80}));
compact 用於各屬性獨佔一行顯示
depth 用於控制顯示層級,默認是2
breakLength用於一行文字的最大個數,超出換行
更詳細的參數及釋義參見官網api
使用util.deprecate方法能夠針對廢棄的api在終端輸出廢棄的提示信息
const util = require('util'); const oldFn = () => { console.log('old fn'); }; class oldClass { constructor() { console.log('old class'); } } const fn1 = util.deprecate(oldFn, 'deprecated fn'); const fn2 = util.deprecate(oldClass, 'deprecated class'); fn1(); new fn2(); // 輸出 old fn old class (node:18001) DeprecationWarning: deprecated fn (node:18001) DeprecationWarning: deprecated class
function deprecate(fn, msg, code) { let warned = false; function deprecated(...args) { if (!warned) { warned = true; if (code !== undefined) { if (!codesWarned[code]) { process.emitWarning(msg, 'DeprecationWarning', code, deprecated); // emit警告信息 codesWarned[code] = true; // 避免同一errorCode重複提示 } } else { process.emitWarning(msg, 'DeprecationWarning', deprecated); } } if (new.target) { // class類型 return Reflect.construct(fn, args, new.target); } return fn.apply(this, args); // 函數類型 } return deprecated; }
命令行選項 | process屬性 | |
---|---|---|
不輸出警告信息 | --no-deprecation --no-warnings |
noDeprecation |
輸出詳細的堆棧信息 | --trace-deprecation --trace-warnings |
traceDeprecation |
拋出錯誤異常 | --throw-deperecation | throwDeprecation |
除了前面提到的三方面的工具外,util.types下還提供了很是豐富的類型識別的方法(isGeneratorFunction、isWeakSet、isPromise、isArrayBuffer)等,之後在須要類型判斷的時候能夠考慮使用util模板提供的這些方法。