简体   繁体   中英

difference between 'util.promisify(setTimeout)' and 'ms => new Promise(resolve => setTimeout(resolve, ms))'

Environment: node 8.11.x I want use async/await for sleep a while.

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

This works.

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

It cause a exception: TypeError: "callback" argument must be a function. setTimeout The document note: This method has a custom variant for promises that is available using util.promisify()

So what's the difference?

promisify is expects a function that has final argument that is the callback.

In other a words it wants a function that looks like:

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

Of course setTimout is the opposite. The argument you want to pass in is the last argument. So when you try to call the promisify d function and pass in an argument, it will take that argument -- the delay-- and try to call it like a function. Of course that's an error.

For entertainment (and slight educational) purposes only, you can pass in a function that reverses the arguments and it will work:

let util = require('util')

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

Now the final argument of the function you passed to promisify expects function. But the asyn/await method is so much nicer…

You know that this right here works:

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

;(async () => {

  const ts = Date.now()

  await sleep(5000)

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

})();

This works fine, why not go with it and use it???

This can be a one-liner: await promisify(setTimeout)(1000) .

It works because setTimeout has a custom variant for promisify . It does work with node 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
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))

Is the essence of the implementation of util.promisify(setTimeout) , but there are additional features to the promisify implementation:

  • it takes an optional value argument that is returned as the value of the promise
setTimeoutPromisify(10, 'foo bar').then(value => console.log(value)) // logs "foo bar"
console.log(await setTimeoutPromisify(10, 'foo bar')) // logs "foo bar"
  • it takes an (optional) options argument that
    • lets you specify an AbortController signal to cancel the timeout
    • lets you choose whether or not to keep a ref , which if set to false, means the program will exit before the timeout finishes if nothing else is happening
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()

For more information see https://nodejs.org/api/timers.html

The implementation in node 16 is below:

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;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM