How to return a Promise with setInterval()

陌路散爱 提交于 2020-05-28 11:51:09

问题


I am trying to return a Promise object ever 1000ms, but i am not sure how to access the data returned in the Promise when it is inside a setInterval() callback.

EDIT I appears i was not being very clear as to my intentions, so i will try and explain what it is i am trying to do. I making a count down where by the necessary calculations are are don every 1000ms based on specified end date.

Here is the code that provides the return value i would like returned as a Pormise value every 1000ms:

calculateTimeRemaining(endDate: string) {
            const { secondsInDay, daysOfYear, secondsInHour, secondsInMinute } = this.unitsOfTime;
            let distance: number =
                (Date.parse(new Date(endDate).toString()) - Date.parse(new Date().toString())) / this.increment;

            if (distance > 0) {
                // Years left
                if (distance >= daysOfYear * secondsInDay) {
                    // 365.25 * 24 * 60 * 60
                    this.timeRemaining.years = Math.floor(distance / (daysOfYear * secondsInDay));
                    distance -= this.timeRemaining.years * daysOfYear * secondsInDay;
                }
                // Days left
                if (distance >= secondsInDay) {
                    // 24 * 60 * 60
                    this.timeRemaining.days = Math.floor(distance / secondsInDay);
                    distance -= this.timeRemaining.days * secondsInDay;
                }
                // Hours left
                if (distance >= secondsInHour) {
                    // 60 * 60
                    this.timeRemaining.hours = Math.floor(distance / secondsInHour);
                    distance -= this.timeRemaining.hours * secondsInHour;
                }
                // Minutes left
                if (distance >= secondsInMinute) {
                    // 60
                    this.timeRemaining.minutes = Math.floor(distance / secondsInMinute);
                    distance -= this.timeRemaining.minutes * secondsInMinute;
                }
                // Seconds left
                this.timeRemaining.seconds = distance;
            }
            return this.timeRemaining;
        }

Example:

    const interval = window.setInterval(() => {
        return new Promise((resolve, reject) => {
            resolve('Hello');
        });
    }, 1000);

How to i access the Promise object with .then() afterwards?

Does not work:

interval.then((data) => console.log(data);

回答1:


What you are looking for is an Observable, not a Promise. With promises, the callback you pass to then is executed at most once, so this:

interval.then((data) => console.log(data));

...will never print "Hello" more than once, even if you corrected the following mistakes in your code:

  • Whatever you return in a setInterval callback function is ignored.
  • setInterval does not return a promise, but an integer number, uniquely identifying the interval timer that was created.

On the other hand, an Observable can emit multiple events, contrary to a Promise.

There is an Observable proposal for EcmaScript, but you could create your own -- very simplified -- version of it:

class Observable {
    constructor(exec) {
        this.listeners = new Set;
        exec({
            next: (value) => this.listeners.forEach(({next}) => next && next(value)),
            error: (err) => this.listeners.forEach(({error}) => error && error(err)),
            complete: () => this.listeners.forEach(({complete}) => complete && complete())
        });
    }
    subscribe(listeners) {
        this.listeners.add(listeners);
        return { unsubscribe: () => this.listeners.delete(listeners) }
    }
}

// Create an Observable instead of a Promise;
const interval = new Observable(({next}) => {
    setInterval(() => next("Hello"), 1000);
});

// Subscribe to that Observable
const subscription = interval.subscribe({ next: (data) => console.log(data) });

// Optionally use the returned subscription object to stop listening:
document.querySelector("button").addEventListener("click", subscription.unsubscribe);
<button>Stop listening</button>

Note that several JavaScript frameworks have an implementation of Observable.




回答2:


Depending on what you're actually trying to do, an async iterable might do the job.

The difference is that an async iterable will only generate the next promise if you consume the last one. Intervals in JavaScript are tricky, even without promises. They try to run their callback at regular intervals, but the execution of any callback may be delayed if the interpreter is busy. That delay will not propagate, though. Also, short intervals will be throttled for background tabs.

Assuming your code is always waiting to consume the async iterable (e.g. in a for…of loop), you could do this:

function delay(t) {
  return new Promise(resolve => setTimeout(resolve, t))
}

async function *interval(t) {
  while(true) {
    let now = Date.now()
    yield "hello"
    await delay(now - Date.now() + t)
  }
}

for await(const greeting of interval(1000)) console.log(greeting)



回答3:


As already mentioned in the comments that you can not return promises on intervals, but you can keep them in a global object and use later,

const jobs = []
const interval = setInterval(() => {
	if(jobs.length == 10) {
		clearInterval(interval);
	}
	let job = Promise.resolve('new job created');
	jobs.push(job);
	console.log('job created')
}, 1000);

setTimeout(() => {
	Promise.all(jobs).then(data => console.log(data))
}, 1000*15);



回答4:


I'm not sure if this will help but; Any function can be made into a promise and the alternative syntax [async keyword] might be useful for you in this case.

async function test() {
      return "hello";
}

test().then( returned => console.log(returned)) // logs hello

setInterval() however does not return a return value rather it returns a "handle".

handle = window . setInterval( handler [, timeout [, arguments ] ] )

... https://www.w3.org/TR/2011/WD-html5-author-20110705/spec.html#timers

You can, however, make promises from an setinterval ...

interval = window.setInterval(makepromise,1000)
async function makepromise() {
    console.log("hello");
}

// or

interval = window.setInterval(async function () {console.log("hello");},1000)

But there is no place for a then then. We are back to callbacks, which we were trying to avoid! But there is functionality perhaps that we can use await within this function.

Better to make your calculateTimeRemaining to a promise And then you can use the then on the interval.

interval = window.setInterval(gameloop,1000);

    function gameloop(endDate: string) {
        calculateTimeRemaining(endDate: string).then(
//
// my then code goes here.
//
        )
    }

async calculateTimeRemaining(endDate: string) {
            const { secondsInDay, daysOfYear, secondsInHour, secondsInMinute } = this.unitsOfTime;
            let distance: number =
                (Date.parse(new Date(endDate).toString()) - Date.parse(new Date().toString())) / this.increment;

            if (distance > 0) {
                // Years left
                if (distance >= daysOfYear * secondsInDay) {
                    // 365.25 * 24 * 60 * 60
                    this.timeRemaining.years = Math.floor(distance / (daysOfYear * secondsInDay));
                    distance -= this.timeRemaining.years * daysOfYear * secondsInDay;
                }
                // Days left
                if (distance >= secondsInDay) {
                    // 24 * 60 * 60
                    this.timeRemaining.days = Math.floor(distance / secondsInDay);
                    distance -= this.timeRemaining.days * secondsInDay;
                }
                // Hours left
                if (distance >= secondsInHour) {
                    // 60 * 60
                    this.timeRemaining.hours = Math.floor(distance / secondsInHour);
                    distance -= this.timeRemaining.hours * secondsInHour;
                }
                // Minutes left
                if (distance >= secondsInMinute) {
                    // 60
                    this.timeRemaining.minutes = Math.floor(distance / secondsInMinute);
                    distance -= this.timeRemaining.minutes * secondsInMinute;
                }
                // Seconds left
                this.timeRemaining.seconds = distance;
            }
            return this.timeRemaining;
        }

However, the value of promises is to avoid callback hell with an excessively complex call back scheme ... where the code is calling back, from call backs, from call backs, etc, etc, etc.

Promises do not operate in a 2nd operating system thread like a webworker. So unless you are attempting to clean up callbacks to make code readable or are actually waiting for something there is no benefit to make use of promises.

setInterval is a clean callback. The Gameloop example is not easier to read and understand because a promise was used. I would suggest in this case it is harder to read. at this point ... unless there are other awaits within the loop or a series of promises that do not need to run synchronously;




回答5:


For interval you can define the interval function like this

 function interval() {
    return new Promise(function(resolve, reject) {
        setInterval(function() {                 
           resolve('Hello');
        }, 1000)
  })
};

For interval you can use this:

way 1:

interval().then((x) => {
   console.log(x);

})

way 2:

const intervalId = setInterval(() => {
   interval().then((x) => {
   console.log(x);

}, 1000) })

This is just for stop the interval function after some time. you must clear the interval if you does not need it more.

setTimeout(() => {
    clearInterval(intervalId);
}, 10000);



回答6:


setInterval already return an integer which is useful to cancel this interval, using clearInterval.

const promise = new Promise((resolve, reject) => {
        resolve('Hello');
    });

Then use it like


promise.then((result) => {
 console.log(result) // Says 'Hello' and will not resolve another value if we call it as it has already been resolved
})

Maybe this is what you tried to achieve. If you want to call it with an interval of 1000 ms.

const getPromiseInstance = () => new Promise((resolve, reject) => {
        resolve(Math.random());
    });

setInterval(() => {
    getPromiseInstance().then((result) => {
      console.log(result)
    })
}, 1000)

You should take a look to Observable, maybe it will fit your needs



来源:https://stackoverflow.com/questions/55759643/how-to-return-a-promise-with-setinterval

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!