简体   繁体   中英

Launch a promise every X seconds

I'm pulling data from 3 differents APIs, and I want to merge all these results into one array.

I guess the proper way to do this is to use Promises:

var function1 = new Promise((resolve, reject)=>{
    ...
    resolve();
});
var function2 = new Promise((resolve, reject)=>{
    ...
    resolve();
});
var function3 = new Promise((resolve, reject)=>{
    ...
    resolve();
});

Promise.all([function1, function2, function3]).then(function(values){
    // Values are all here!
});

How can I call all the promises again and join them via Promise.all every second?

I've tried

setInterval(function(){
    Promise.all([function1, function2, function3]).then(function(values){
        // Values are all here and up to date!
    });
}, 1000)

without success.

Thanks for your help!

You need to recreate the Promise objects every time you want to invoke them:

var function1 = (resolve, reject)=>{
    console.log('calling 1');
    resolve();
};
var function2 = (resolve, reject)=>{
    console.log('calling 2');
    resolve();
};
var function3 = (resolve, reject)=>{
    console.log('calling 3');
    resolve();
};

setInterval(function(){
    Promise.all([new Promise(function1), new Promise(function2), new Promise(function3)]).then(function(values){
        console.log('alldone');
    });
}, 1000)

This is because the promise is only executed upon creation, and otherwise in your loop you're just attaching a new then() method which will not call your API.

EDIT: Be advised that setInterval , as shown, will fire three requests to your API every 1 second. That's a pretty fast rate of fire and is likely to get you in trouble unless both your API and the network are blazing fast. A more sensible approach might be to only fire the next request once the previous one has been handled. To do that, simply substitute the setInterval call with this:

var callback = function(){
    Promise.all([new Promise(function1), new Promise(function2), new Promise(function3)]).then(function(values){
        console.log('all done');
        setTimeout(callback, 1000);
        console.log('next call successfully enqued');
    });
};
setTimeout(callback, 1000);

Thanks to Kevin B for pointing this out.

Make sure you call the API each time (by creating new Promise).

 /** * Create function that return a NEW Promise each time. * Once resolved a promise won't change state */ const function1 = () => new Promise((resolve, reject)=>{ // something resolve('1 - ' + new Date()); }); const function2 = () => new Promise((resolve, reject)=>{ // something resolve('2 - ' + new Date()); }); const function3 = () => new Promise((resolve, reject)=>{ // something resolve('3 - ' + new Date()); }); /** * For the setInterval callback, create a function * that will return a new Promise from all the previous promises */ const all = () => Promise.all([ function1(), function2(), function3() ]).then(function(values){ console.log(values); return values; }); setInterval(all, 1000);

Answer

This question was in my interview and I had trouble that it should be implemented only using class, maybe you will find it useful for you and rewrite it using functions.

 class HTTPService { constructor(base = "", strategy = "default" | "queue", promises = []) { this.base = base; this.strategy = strategy; this.promises = 0; } urlSerializer(payload) { return `?${Object.entries(payload) .map((el) => el.join("=")) .join("$")}`; } returnDefaultPromise(path) { return new Promise((resolve) => setTimeout(() => { resolve(path); }, 1000) ); } returnQueuePromise(path) { return new Promise((resolve) => setTimeout(() => { this.promises -= 1000; resolve(path); }, this.promises) ); } get(url, payload) { let serialized = payload ? this.urlSerializer(payload) : ""; if (!url) throw new Error("Please add url to function argument"); switch (this.strategy) { case "default": return this.returnDefaultPromise(`${this.base}/${url}${serialized}`); case "queue": this.promises += 1000; return this.returnQueuePromise(`${this.base}/${url}${serialized}`); default: return new Promise((resolve) => resolve(`${this.base}/${url}${serialized}`) ); } } } const httpService = new HTTPService("http://test.com", "queue"); const httpService2 = new HTTPService("http://test.com", "default"); const br = document.createElement('br'); let div = document.createElement('div'); let content = document.createTextNode(''); httpService.get("/api/me").then((data) => { content = document.createTextNode(data); div.appendChild(content); div.appendChild(br); console.log(data); }); // should be 'http://test.com/api/me' // B: httpService.get("/api/test", { foo: 1, test: 2 }).then((data) => { content = document.createTextNode(data); div.appendChild(content); div.appendChild(br); console.log(data); // should be 'http://test.com/api/test?foo=1&test=2' }); // C: httpService.get("/api/test", { baz: 10, case: "some" }).then((data) => { content = document.createTextNode(data); div.appendChild(content); div.appendChild(br); console.log(data); // should be 'http://test.com//api/test?baz=10$case=some' }); // D: httpService.get("/api/test", { bar: 1, dummy: "text" }).then((data) => { content = document.createTextNode(data); div.appendChild(content); div.appendChild(br); console.log(data); // should be 'http://test.com//api/test?bar=1$dummy=text' }); httpService2.get("/app/test").then((data) => { content = document.createTextNode(data); div.appendChild(br); div.appendChild(content); div.appendChild(br); console.log(data); }); document.querySelector('#result').appendChild(div)
 <div id="result"></div>

This example to call functions

Also you can check analogy in react application through codesandbox

Solution

Every time you need to create promises again and resolve them.

 setInterval(function(){ var function1 = new Promise((resolve, reject)=>{ resolve(new Date()); }); var function2 = new Promise((resolve, reject)=>{ resolve(2); }); var function3 = new Promise((resolve, reject)=>{ resolve(3); }); Promise.all([function1, function2, function3]).then(function(values){ console.log(values); }); }, 1000)

Run the above code its working!!!

An alternative to setInterval() is using setTimeout() inside a recursive loop:

function joinedResults() {
    Promise.all([function1, function2, function3])
        .then(function(values){
            console.log(values);
            setTimeout(()=>{ joinedResults() }, 1000);
        });
}

joinedResults();

This stackoverflow post has a similar question.

In order to chain your promise outside of setInterval , you can wrap it in a function:

let repeat = (ms, func) => new Promise(r => (setInterval(func, ms), wait(ms).then(r)));

repeat(1000, () => Promise.all([myfunction()])
      .then(...)

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