'util.promisify(setTimeout)'和'ms => new Promise(resolve => setTimeout(resolve,ms))之间的区别

时间:2018-08-11 02:42:04

标签: javascript node.js

环境:节点8.11.x 我想使用async / await睡眠一会儿。

const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
await sleep(5000)

这有效。

const sleep = util.promisify(setTimeout)
await sleep(5000)

它会导致异常:TypeError:“回调”参数必须是一个函数。 setTimeout文档注释:此方法具有一个针对promise的自定义变体,可使用util.promisify()

那有什么区别?

4 个答案:

答案 0 :(得分:1)

这可以是一个单行:await promisify(setTimeout)(1000)

它起作用,因为setTimeout有一个custom variant for promisify。确实适用于节点8.11。

nvm install 8.11 && nvm use 8.11
node <<HEREDOC
  (async () => {
    // const start = Date.now();
    await require('util').promisify(setTimeout)(5000);
    // console.log(Date.now() - start);
  })()
HEREDOC

答案 1 :(得分:0)

promisify期望一个函数的最终参数为回调。

换句话说,它想要一个看起来像这样的函数:

function takesACallback(str, Fn) {
    Fn(null, "got: ", str)
    // or with an error:
    // Fn(error)
}

当然setTimout相反。您要传递的参数是最后一个参数。因此,当您尝试调用promisify d函数并传入一个参数时,它将采用该参数-延迟-并尝试像函数一样调用它。当然,这是一个错误。

仅出于娱乐(和轻微的教育目的)目的,您可以传入一个可以反转参数的函数,它将起作用:

let util = require('util')

let pause = util.promisify((a, f) => setTimeout(f, a))
pause(2000)
.then(() => console.log("done"))

现在,您传递给promisify的函数的最后一个参数需要函数。但是asyn/await方法如此好得多……

答案 2 :(得分:0)

您知道这里的功能有效:

const {promisify} = require('util');
const sleep = promisify(setTimeout);

;(async () => {

  const ts = Date.now()

  await sleep(5000)

  console.log(Date.now()-ts)

})();

这很好用,为什么不去使用它呢?

答案 3 :(得分:0)

const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))

util.promisify(setTimeout) 实现的本质,但 promisify 实现还有其他功能:

  • 它接受一个可选的 value 参数,作为 promise 的值返回
setTimeoutPromisify(10, 'foo bar').then(value => console.log(value)) // logs "foo bar"
console.log(await setTimeoutPromisify(10, 'foo bar')) // logs "foo bar"
  • 它需要一个(可选的)选项参数
    • 允许您指定 AbortController signal 以取消超时
    • 让您选择是否保留 ref,如果设置为 false,则表示如果没有其他事情发生,程序将在超时完成之前退出
const controller = new AbortController();
const signal = ac.signal;

setTimeoutPromisify(1000, 'foo bar', { signal, ref: true })
  .then(console.log)
  .catch((err) => {
    if (err.name === 'AbortError')
      console.log('The timeout was aborted');
  })

controller.abort()

有关详细信息,请参阅 https://nodejs.org/api/timers.html

节点 16 中的实现如下:

function setTimeout(after, value, options = {}) {
  const args = value !== undefined ? [value] : value;
  if (options == null || typeof options !== 'object') {
    return PromiseReject(
      new ERR_INVALID_ARG_TYPE(
        'options',
        'Object',
        options));
  }
  const { signal, ref = true } = options;
  try {
    validateAbortSignal(signal, 'options.signal');
  } catch (err) {
    return PromiseReject(err);
  }
  if (typeof ref !== 'boolean') {
    return PromiseReject(
      new ERR_INVALID_ARG_TYPE(
        'options.ref',
        'boolean',
        ref));
  }

  if (signal && signal.aborted) {
    return PromiseReject(new AbortError());
  }
  let oncancel;
  const ret = new Promise((resolve, reject) => {
    const timeout = new Timeout(resolve, after, args, false, true);
    if (!ref) timeout.unref();
    insert(timeout, timeout._idleTimeout);
    if (signal) {
      oncancel = FunctionPrototypeBind(cancelListenerHandler,
                                       timeout, clearTimeout, reject);
      signal.addEventListener('abort', oncancel);
    }
  });
  return oncancel !== undefined ?
    PromisePrototypeFinally(
      ret,
      () => signal.removeEventListener('abort', oncancel)) : ret;
}