Node.js util 模块解读

Node模块解读第四篇util模块,之前已经介绍过vm模块、 Buffer模块  Event模块 

util模块最初的目的是为内部API提供一些工具支持,然而很多工具函数对于普通的开发者来说也十分有用,因此util模块将一些方法实现了对外暴露。本文主要探讨以下三方面的工具函数:

前端学习

. 风格转换

. 调试输出

. 废弃API

风格转换

callback转换promise

针对传入error-first回调作为函数的最后一个参数的函数(比如fs.readFile('./filename', (err, data) => {}), util提供了promisify(original)方法用来将这种类型的函数转换成返回promise的形式。

fs.readFile为例

const fs = require('fs');

扫描二维码关注公众号,回复: 1083511 查看本文章

 

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,简易版本的代码如下:

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调用之前

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

  });

promisecallback

utilcallbackify方法与promisify刚好相反,callbackify用于把async(或者返回promise)的函数转换成遵从error-first回调风格的类型

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

});

有一种情况需要关注,假如promisereject(null || 0 || false)的话,那么callbackerr判断为非,程序会继续执行console.log(data),这其实是不正确的。因此callbackify对于这种情况做了特殊的处理(创建一个error,将原始的信息放在errorreason属性上,把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;

}

调试输出

util.debuglog(section)

debuglog方法用于根据NODE_DEBUG环境变量来选择性的输出debug信息,例如下面的例子

//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

NODE_DEBUG=fs,net,tls // 多个section

NODE_DEBUG=foo*  // 通配符

上面的debuglog函数执行的时候支持占位符,其实底层使用的是util.format方法。

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

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, { compactfalsedepthnullbreakLength80}));

compact 用于各属性独占一行显示

depth 用于控制显示层级,默认是2

breakLength用于一行文字的最大个数,超出换行

更详细的参数及释义参见官网api

废弃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


猜你喜欢

转载自blog.csdn.net/qq_32506555/article/details/80456591