In my code below, I would like to execute a, b and c but then a1 since a1 is added within a. However, it doesn't look like Promise.all is updated with the new a1 added. What's the best way to do this? Is there Promise all update? I try not to do await a1 within a.
var arr = [];
async function a() {
return new Promise(resolve => {
setTimeout(() => {
console.log("Resolve a");
arr.push(a1);
resolve(1);
}, 2000);
});
}
async function b() {
return new Promise(resolve => {
setTimeout(() => {
console.log("Resolve b");
resolve(2);
}, 4000);
});
}
async function c() {
return new Promise(resolve => {
setTimeout(() => {
console.log("Resolve c " + arr.length);
resolve(3);
}, 6000);
});
}
async function a1() {
return new Promise(resolve => {
setTimeout(() => {
console.log("Resolve a1");
resolve(11);
}, 2000);
});
}
arr = [a(), b(), c()];
(async function run() {
await Promise.all(arr);
})();
console.log('Done');
For a start, you push a1 where I think you'd want to push a1() - otherwise you're pushing a function, not a promise
But that won't change anything, because the array passed to Promise.all is (in every library I've seen) copied (using Array#slice) so any changes to the passed in array won't be "visible" to the Promise.all (internal) code
But, you can create a function that recursively calls Promise.all on the array until the length of the results is equal to the (current, new) length of the array
var recursiveAll = a => Promise.all(a).then(r => r.length == a.length ? r : recursiveAll(a));
var arr = [];
async function a() {
return new Promise(resolve => {
setTimeout(() => {
console.log("Resolve a");
arr.push(a1());
resolve(1);
}, 200);
});
}
async function b() {
return new Promise(resolve => {
setTimeout(() => {
console.log("Resolve b");
resolve(2);
}, 400);
});
}
async function c() {
return new Promise(resolve => {
setTimeout(() => {
console.log("Resolve c " + arr.length);
resolve(3);
}, 600);
});
}
async function a1() {
return new Promise(resolve => {
setTimeout(() => {
console.log("Resolve a1");
resolve(11);
}, 200);
});
}
arr = [a(), b(), c()];
(async function run() {
let results = await recursiveAll(arr);
console.log(results);
})();
Interesting question...
You may indeed modify Promise.all() on the run by replacing a promise with another one even if the newly inserted one resolves the latest.
In the below example promise three will replace itself with a new promise which will resolve in an additional 2000ms later and Promise.all() will wait until it to resolves to include it's resolving value to the array provided to the .then() stage.
var fun = _ => new Promise((v,x) => setTimeout(v,2000,5)),
one = new Promise((v,x) => setTimeout(v,1000,1)),
two = new Promise((v,x) => setTimeout(v,2000,2)),
three = new Promise((v,x) => setTimeout(v,3000,fun())),
four = new Promise((v,x) => setTimeout(v,4000,4));
Promise.all([one,two,three,four])
.then(a => console.log(a));
Please wait for 5 seconds to see the result.
Related
Result: a, b, d, c.
Expected: a, b, c, d
const promises = []
console.log('a')
someFunc(promises)
Promise.allSettled(promises).then(() => console.log('d'))
function someFunc(promises) {
const promise = new Promise(resolve => setTimeout(() => {
console.log('b')
const promise2 = new Promise(resolve2 => setTimeout(() => {
console.log('c')
resolve2()
}, 3000))
promises.push(promise2)
resolve()
}, 3000))
promises.push(promise)
return promise
}
While it'd be possible to patch it up by not pushing to an array, but instead having each Promise chain off of each other in someFunc...
console.log('a')
someFunc().then(() => console.log('d'))
function someFunc() {
return new Promise(resolve => setTimeout(() => {
console.log('b')
new Promise(resolve2 => setTimeout(() => {
console.log('c')
resolve2()
}, 3000))
.then(resolve);
}, 3000))
}
A much more understandable version would promisify setTimeout to begin with instead of doing it every time.
const setTimeoutPromise = ms => new Promise(resolve => setTimeout(resolve, ms));
console.log('a')
someFunc().then(() => console.log('d'))
function someFunc() {
return setTimeoutPromise(3000)
.then(() => {
console.log('b');
return setTimeoutPromise(3000)
})
.then(() => {
console.log('c');
return setTimeoutPromise(3000)
});
}
Which can be further simplified with await...
const setTimeoutPromise = ms => new Promise(resolve => setTimeout(resolve, ms));
console.log('a')
someFunc().then(() => console.log('d'))
async function someFunc() {
await setTimeoutPromise(3000);
console.log('b');
await setTimeoutPromise(3000);
console.log('c');
await setTimeoutPromise(3000);
}
Solution
I know you may be confused on this question a lot. Here is the solution I found
const promises = []
const timeout = ms => {
const promise = new Promise(resolve => setTimeout(resolve, ms))
promises.push(promise)
return promise
}
const someFunc = () =>
timeout(1000).then(() => {
console.log('b')
timeout(1000).then(() => console.log('c'))
})
async function main() {
console.log('a')
someFunc()
let i = 0;
while (promises.length > i) {
i = promises.length
await Promise.allSettled(promises)
}
console.log('d')
}
main()
The problem I really want to express is that the content of the function someFunc is not determined at compile time while I need to make sure that the function leave no side effect once executed.
var functionsArray = [
function() {
setTimeout(function() {
console.log(1);
}, 100);
},
function() {
setTimeout(function() {
console.log(2);
}, 200);
},
function() {
setTimeout(function() {
console.log(3);
}, 10);
}
],
Say I have an array of functions like above(the number of functions in it is not known). I want to write a function which takes this array as parameter and executes them in sequence. In the example above, I want it to log 1,2,3 in the sequence. Since Promise.all does not guarantee the order of execution, is it possible to achieve this without callback hell?
You can't get a promise from a function that just calls setTimeout - it needs some help, e.g.:
function after(n, f) {
return () => new Promise(resolve => {
setTimeout(() => {
resolve(f());
}, n);
});
}
with usage:
var functionsArray = [
after(100, () => console.log(1)),
after(200, () => console.log(2)),
after( 10, () => console.log(3)),
];
With that array you can then just await each function in turn:
for (let f of functionsArray) {
await f();
}
You can write an simple setTimeoutPromise function:
function timeoutPromise(time = 0){
return new Promise(resolve => setTimeout(resolve, time)
}
await timeoutPromise(10);
console.log('waited 10 sec')
timeoutPromise(20).then(() => console.log('waited 20 sec')
//make a new array as you like
Promise.all([
timeoutPromise(100).then(() => console.log(1)),
timeoutPromise(200).then(() => console.log(2)),
timeoutPromise(300).then(() => console.log(3))
])
Hello I'm new to javascript and wondering if there is a way to covnert below setInterval thingy into a promise so that .then could be used instead of the callback. Any help?
My ideas:
With a setTimeout I could resolve after a fixed time. But I'm not getting any ideas dealing with setInterval...
function alert_above(scrip, price, callback) {
var intvl = setInterval(() => {
if (get_last_price(scrip) > price) {
callback();
clearInterval(intvl);
}
}, 1000);
return intvl;
}
You can create a promise function that resolves asynchronously. Read more about Promise Here
function myInterval() {
return new Promise(resolve => {
const intId = setInterval(() => {
clearInterval(intId);
resolve();
}, 1000)
})
}
myInterval().then(() => {
console.log('Called after 1 second');
})
I think you could wrap into a new Promise like :
function promisifySetInterval(time) {
var defer = new Promise(resolve => {
let counter = 0
var intvl = setInterval(() => {
if (counter > time) {
resolve('hey');
clearInterval(intvl);
} else {
counter += 1000
}
}, 1000);
})
return defer;
}
promisifySetInterval(2000).then(param => {
console.log('hey', param)
})
And for youre case something like this :
function alert_above(scrip, price) {
var defer = new Promise(resolve => {
var intvl = setInterval(() => {
if (get_last_price(scrip) > price) {
resolve('hey');
clearInterval(intvl);
}
}, 1000);
})
return defer;
}
alert_above().then(param => {
console.log('hey', param)
})
Note: poll will be executed without delay the first time, which is different from the native setInterval.
Q: Why is poll based on setTimeout not setInterval?
A: Please see Execute the setInterval function without delay the first time.
Implementation:
// Promisify setTimeout
const pause = (ms, cb, ...args) =>
new Promise((resolve, reject) => {
setTimeout(async () => {
try {
resolve(await cb?.(...args))
} catch (error) {
reject(error)
}
}, ms)
})
// Promisify setInterval
const poll = async (interval, times, cb, ...args) => {
let result
const resolve = value => (times = 0) || (result = value)
const reject = reason => (times = 0) || (result = Promise.reject(reason))
await (async function basePoll() {
if (times > 0) {
const _result = await cb(...args, resolve, reject)
if (times) {
result = _result
--times && (await pause(interval, basePoll))
}
}
})()
return result
}
Tests:
import ordinal from 'ordinal'
// Test 1
poll(1000, 3, (a, b, c) => [a, b, c], 1, 2, 3).then(value => console.log(value))
// Test 2
let times = 0
poll(1000, 5, resolve => {
console.log(`${ordinal(++times)} time`)
times === 3 && resolve('resolved')
}).then(value => console.log(value))
// Test 3
let times = 0
poll(1000, 5, (resolve, reject) => {
console.log(`${ordinal(++times)} time`)
times === 3 && reject('rejected')
}).catch(error => console.error(error))
This question already has answers here:
Combination of async function + await + setTimeout
(17 answers)
Closed 7 months ago.
I have a async function that waits for an axios call to complete before proceeding. The problem is that I need to put a timeout on the axios call to half a second so that I don't hit the shopify API call limit.
async function processMatchingSchools(customer_metafield_url) {
for (const item of customer_metafield_url) {
await axios.get(item).then((res) => {
for (key in res.data.metafields) {
if (res.data.metafields[key].value === schoolName) {
id_for_each_student.push(shopifyAdmin + "/customers/" + res.data.metafields[key].owner_id + "/metafields.json")
}
}
})
}
console.log("Customer metafields to search", id_for_each_student)
processOwnerIds(id_for_each_student)
}
when I try putting a setTimeout, it calls the setTimeout and moves on before completing the axios calls.
async function processMatchingSchools(customer_metafield_url) {
for (const item of customer_metafield_url) {
await setTimeout(function(item) {
axios.get(item).then((res) => {
for (key in res.data.metafields) {
if (res.data.metafields[key].value === schoolName) {
id_for_each_student.push(shopifyAdmin + "/customers/" + res.data.metafields[key].owner_id + "/metafields.json")
}
}
})
}, 500)
}
console.log("Customer metafields to search", id_for_each_student)
processOwnerIds(id_for_each_student)
}
Any help?
await only works on promises.
You need to wrap setTimeout in a promise:
const waitFor = delay => new Promise(resolve => setTimeout(resolve, delay));
await waitFor(500);
setTimeout() doesn't return a Promise, but you can wrap it in one like this. I also cleaned up the rest of your code a little.
async function processMatchingSchools(customer_metafield_url) {
for (const item of customer_metafield_url) {
await new Promise(resolve => {
setTimeout(resolve, 500)
})
await axios.get(item).then((res) => {
Object.values(res.data.metafields).filter(
({ value }) => value === schoolName
).forEach(({ owner_id }) => {
id_for_each_student.push(`${shopifyAdmin}/customers/${owner_id}/metafields.json`)
})
})
}
console.log("Customer metafields to search", id_for_each_student)
processOwnerIds(id_for_each_student)
}
setTimeout does not return a promise so cannot be awaited.
You could create your own promise-based setTimeout and use that.
const setTimeoutPromise = timeout => new Promise(resolve => {
setTimeout(resolve, timeout);
});
await setTimeoutPromise(500);
Create a sleep function that returns a promise that you can use, like so:
const sleep = (milliseconds=500) => new Promise(resolve => setTimeout(resolve, milliseconds))
And to use it in an async function:
(async () => {
console.log("function invoked...")
await sleep(500)
console.log("I got here about 500 milliseconds later")
})()
You need to to create new promise for example like that
function delay(ms){
return new Promise(resolve => setTimeout(resolve, ms))
}
And then use it in your code before calling API
...
await delay(500)
await axios.get(item).then((res) => {
...
I created setTimeout2 function that works the same just as a promise:
const setTimeout2 = (callback, ms) => {
return new Promise(resolve => setTimeout(() => {
callback();
resolve();
}, ms));
}
So, altogether (noticed the setTimeout2 change):
async function processMatchingSchools(customer_metafield_url) {
for (const item of customer_metafield_url) {
await setTimeout2(function(item) {
axios.get(item).then((res) => {
for (key in res.data.metafields) {
if (res.data.metafields[key].value === schoolName) {
id_for_each_student.push(shopifyAdmin + "/customers/" + res.data.metafields[key].owner_id + "/metafields.json")
}
}
})
}, 500)
}
console.log("Customer metafields to search", id_for_each_student)
processOwnerIds(id_for_each_student)
}
This question already has answers here:
ES6 promises with timeout interval
(6 answers)
Closed 5 years ago.
I want to create one Promise that, internally, calls a series of asynchronous methods in sequence and returns the results of these methods, concatenated in an ordered Array, once the last method returns.
I was trying this:
const getArray = function (thisArray) {
return new Promise ( function (resolve, reject) {
if (thisArray.length < 3) {
setTimeout(function() {
console.log('adding element');
thisArray.push(thisArray.length);
getArray(thisArray);
}, 1000);
} else {
console.log(thisArray);
console.log('resolving');
resolve(thisArray);
}
});
}
getArray([]).then(function(data) {
console.log('thened');
console.log(data);
})
but it doesn't ever calls the then(). How can I do this?
You're not far off at all, you just need to be sure to call resolve for each new Promise. If you're still building the array, you pass the resulting promise from your recursive getArray call; otherwise, you pass the array:
const getArray = function(thisArray) {
return new Promise((resolve, reject) => {
if (thisArray.length >= 3) {
// All done
resolve(thisArray);
} else {
// Do something async...
setTimeout(() => {
// ...add the result
console.log('adding element');
thisArray.push(thisArray.length);
// ...and recurse
resolve(getArray(thisArray));
}, 1000);
}
});
}
getArray([]).then(data => {
console.log('done');
console.log(data);
});
That's a bit cleaner if you separate the function doing the async work from getArray, and ensure that the function doing the async work returns a promise. For our purposes, we can just wrap setTimeout in a Promise wrapper:
const setTimeoutP = (cb, delay) => {
return new Promise(resolve => {
setTimeout(() => {
resolve(cb());
}, delay);
});
};
then:
const getArray = thisArray => {
if (thisArray.length >= 3) {
return Promise.resolve(thisArray);
}
return setTimeoutP(() => {
console.log("adding element");
thisArray.push(thisArray.length);
});
};
Live copy:
const setTimeoutP = (cb, delay) => {
return new Promise(resolve => {
setTimeout(() => {
resolve(cb());
}, delay);
});
};
const getArray = thisArray => {
if (thisArray.length >= 3) {
return Promise.resolve(thisArray);
}
return setTimeoutP(() => {
console.log("adding element");
thisArray.push(thisArray.length);
return getArray(thisArray);
}, 1000);
};
getArray([]).then(data => {
console.log('done');
console.log(data);
});