1

This is not a duplicate of this similar question here.

Instead I want the API or interface to work as follows:

setTimeoutAsync(1000).then(()=>{
  console.log('1 second passed');
});

I want to use new Promise() as from my understanding this is what the constructor is primarily used for:

new Promise((resolve, reject)=>{
})

and setTimeout();

setTimeout(func, time, funcArg1);
  • All snippets proposed in the other question do exactly what you are asking for? – Jonas Wilms Dec 04 '19 at 17:31
  • Please [edit] your question to include the reason(s) you think the duplicate does not apply to your question. – Heretic Monkey Dec 04 '19 at 17:33
  • 2
    To clarify @JonasWilms's comment, the *answers* to that question deal with that OP's desire to use the wrapped timeout a specific way in *promise chaining*, but the OP's version of `promiseTimeout` has the exact interface you've specified in this question already. To reiterate: The first snippet in the dupe target is a wrapped timeout with the exact interface you want. – Klaycon Dec 04 '19 at 17:33
  • 2
    A better dupe target is probably https://stackoverflow.com/questions/38956121/how-to-add-delay-to-promise-inside-then?noredirect=1&lq=1 because the answers to that question answer this question well. I have no idea why that one is marked a dupe of the promise-chaining question, it's quite misleading. – Klaycon Dec 04 '19 at 17:36
  • @klaycon good suggestion, changed the target. – Jonas Wilms Dec 04 '19 at 17:45
  • 1
    @J.M. Yeah, what I was trying to clarify was that the first snippet in the first dupe's question text, as well as the top answer to the second dupe both include a function almost identical to the one answered here, and both serve as fine answers to the stated question. Indeed neither had passing arguments but to be fair that was an extra detail that wasn't known at the time. – Klaycon Dec 04 '19 at 17:49

1 Answers1

2

I think you want something like this:

function setTimeoutAsync(interval, value) {
  return new Promise(resolve => setTimeout(resolve, interval, value));
}

Usage:

setTimeoutAsync(1000).then(() => console.log('1 second passed'));
// or with an argument
setTimeoutAsync(1000, 1).then(n => console.log(`${n} second passed`));

Or using async/await:

async function main() {
  const n = await setTimeoutAsync(1000, 1);
  console.log(`${n} second passed`);
}

This solution is limited to 1 value argument, because of resolve only accepting 1 argument, and you can't retrieve the handle from setTimeout to clear it in case you need that. Thanks to @PatrickRoberts for pointing that out.

If you need more then 1 value argument I recommend passing an array with your values or an object containing key-value pairs.

A1rPun
  • 16,287
  • 7
  • 57
  • 90
  • 1
    or `await setTimeoutAsync(1000)` if inside an `async` function. – nicholaswmin Dec 04 '19 at 17:34
  • @NikKyriakides Good suggestion. I assumed this question wasn't a dup so I answered quickly. – A1rPun Dec 04 '19 at 17:36
  • 2
    @J.M. FYI, `resolve()` only accepts a maximum of one argument so the rest parameter seems rather silly. I'd suggest something like `function setTimeoutAsync(delay, value = undefined) { return new Promise(resolve => { setTimeout(resolve, delay, value); }); }` – Patrick Roberts Dec 04 '19 at 17:45
  • @PatrickRoberts Good catch, I edited the answer. – A1rPun Dec 04 '19 at 21:17
  • @A1rPun I think you misunderstood my point. It doesn't particularly matter to me whether you pass the arguments through `setTimeout()`, or directly to `resolve()` within a closure. What I'm saying is that there's no point in making it variadic because a specification-compliant promise will drop all but `args[0]`, e.g. `new Promise(resolve => resolve(1, 2, 3)).then((...args) => console.log(...args))` will log `1`, not `1 2 3` – Patrick Roberts Dec 04 '19 at 21:20
  • @PatrickRoberts Yes you are right, I made a quick assumption and while I'm testing my current implementation I see your point very clearly. – A1rPun Dec 04 '19 at 21:25
  • 1
    For cancellation, you could potentially monkeypatch the promise instance like this: `function setTimeoutAsync(delay, value) { let timeout; return Object.assign(new Promise(resolve => { timeout = setTimeout(resolve, delay, value); }), { cancel () { clearTimeout(timeout); } }); }`, though based on the nature of the question I don't think this was even a concern, and I don't recommend the practice of creating promises that never resolve, since it's often code smell and can lead to confusing or unexpected behavior in higher-level promise chains that follow this underlying promise. – Patrick Roberts Dec 04 '19 at 21:44
  • @PatrickRoberts That's a creative monkey patch! I want to try to get as much functionality out of this answer though. Maybe you could `reject` the promise when the timeout gets cancelled, I think that doesn't smell but the implementation will be some dirty code. – A1rPun Dec 04 '19 at 21:51
  • @A1rPun `function setTimeoutAsync(delay, value) { let rejectImpl; return Object.assign(new Promise((resolve, reject) => { rejectImpl = reject; setTimeout(resolve, delay, value); }), { cancel (reason) { rejectImpl(reason); } }); }` in that case. It's safe to let the timeout occur anyway, because once a promise settles, it can't _re_-settle, so continuations (e.g. `.then()`, `.catch()`, `.finally()`) will only be called once, depending on how the promise settles _first_ (i.e. fulfillment or rejection). – Patrick Roberts Dec 04 '19 at 21:55
  • 1
    Looking back at that atrocity of an implementation in my last comment though, I'm inclined to say that's a failure to separate concerns. For cancellation, I'd suggest a completely separate (and composable) API akin to C#'s `TaskCompletionSource`: `class PromiseCompletionSource { constructor () { this.promise = new Promise((resolve, reject) => { this.resolve = resolve; this.reject = reject; }); } }` then with your current implementation, `var pcs = new PromiseCompletionSource(); pcs.promise.then(console.log, console.error); setTimeoutAsync(1000, 1).then(pcs.resolve);` so `pcs.reject()` cancels. – Patrick Roberts Dec 04 '19 at 22:14
  • @PatrickRoberts I really like your idea of keeping it SOLID. I <3 you monkey-patches as well but I wouldn't recommend it in production code :) – A1rPun Dec 04 '19 at 22:31