How to dynamically add new promise to the promises chain - javascript

I want to create promises chain and then dynamically add as many promises to it as it's needed. These additions could be in some cycle with dynamic number of steps so that I can't use chain like .then().then().then... Code bellow works improperly but you'll get the idea. Result should be a console logged 3000, 4000, 5000 numbers in 3, 4 and 5 seconds consequently but actually doesn't work that way. Any ideas?
let launchChain = function(delay)
{
return new Promise((resolve: Function, reject: Function) => {
setTimeout(() => {
console.log(delay);
resolve();
}, delay)
})
}
let chain = launchChain(3000);
chain.then(function () {
return launchChain(4000);
})
chain.then(function () {
return launchChain(5000);
})

So used reduce and this site
var delays = [0, 1000, 2000, 3000, 4000];
function workMyCollection(arr) {
return arr.reduce(function(promise, item) {
return promise.then(function() {
return launchChain(item);
});
// uses this orignal promise to start the chaining.
}, Promise.resolve());
}
function launchChain(delay) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
console.log(delay);
resolve();
}, delay);
});
}
workMyCollection(delays);
[EDIT] : Another way
var delays = [0, 1000, 2000, 3000, 4000];
var currentPromise = Promise.resolve();
for (let i = 0; i < delays.length; i++) {
// let makes i block scope .. var would not have done that
currentPromise = currentPromise.then(function() {
return launchChain(delays[i]);
});
}
function launchChain(delay) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
console.log(delay);
resolve();
}, delay);
});
}
Do let me know if this worked for you :)
thanks to this question I learned a lot!

function run(delay){
let chain = launchChain(delay);
chain.then(function() {
run(delay+1000);
});
}
run(3000);

Thanks sinhavartika! It works! But I actually took example from here
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
and changed it a bit and now I use it in my project in the following way:
/**
* Runs promises from promise array in chained manner
*
* #param {array} arr - promise arr
* #return {Object} promise object
*/
function runPromiseInSequense(arr) {
return arr.reduce((promiseChain, currentPromise) => {
return promiseChain.then((chainedResult) => {
return currentPromise(chainedResult)
.then((res) => res)
})
}, Promise.resolve());
}
var promiseArr = [];
function addToChain(delay)
{
promiseArr.push(function (delay) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(delay);
resolve();
}, delay)
});
}.bind(this, delay))
}
addToChain(1000);
addToChain(2000);
addToChain(3000);
addToChain(4000);
runPromiseInSequense(promiseArr);

Related

Javascript class chaning with Promise [duplicate]

I am trying to make a method sleep(delay) in method chaining. For this I am using setTimeout with Promise. This will require any method following the sleep to be inside the then.
Right now I am calling the function like
lazyMan("John", console.log).eat("banana").sleep(5).then(d => {d.eat("apple");});.
Here is my code
function lazyMan(name, logFn) {
logFn(name);
return {
eat: function(val) {
console.log(val);
return this;
},
sleep: function(timer) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(`Sleeping for ${timer} seconds`);
resolve(this);
}, timer * 1000);
}).then(d => this);
}
};
}
lazyMan("John", console.log)
.eat("banana")
.sleep(5)
.then(d => {
d.eat("apple");
});
Is there a way I can modify my function to call it like lazyMan("John", console.log).eat("banana").sleep(5).eat("apple") and get the output in same order
I have gone through Add a sleep method in a object method chain(JS)
You can keep a promise for your "task queue", so anything that needs to be done, will be added onto there via .then(). This provides a fluent API for scheduling stuff.
function lazyMan(name, logFn) {
logFn(name);
let taskQueue = Promise.resolve();
const addTask = f => {
taskQueue = taskQueue.then(f);
}
return {
eat: function(val) {
addTask(() => console.log(`Eating [${val}]`));
return this;
},
sleep: function(timer) {
addTask(() => new Promise((resolve, reject) => {
console.log(`Start sleeping for ${timer} seconds`);
setTimeout(() => {
console.log(`End sleeping for ${timer} seconds`);
resolve();
}, timer * 1000);
}))
return this;
}
};
}
lazyMan("John", console.log)
.eat("banana")
.sleep(5)
.eat("apple");
Note that this change means that every action is technically asynchronous. However, that's at least uniform, so it's less of a chance of a surprise when keeping it in mind.

How to wait a Promise inside a forEach loop

I'm using some Promises to fetch some data and I got stuck with this problem on a project.
example1 = () => new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo1');
}, 3000);
});
example2 = () => new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo2');
}, 3000);
});
doStuff = () => {
const listExample = ['a','b','c'];
let s = "";
listExample.forEach((item,index) => {
console.log(item);
example1().then(() => {
console.log("First");
s = item;
});
example2().then(() => {
console.log("Second");
});
});
console.log("The End");
};
If I call the doStuff function on my code the result is not correct, the result I expected is shown below.
RESULT EXPECTED
a a
b First
c Second
The End b
First First
Second Second
First c
Second First
First Second
Second The End
At the end of the function no matter how I try, the variable s gets returned as "", I expected s to be "c".
It sounds like you want to wait for each Promise to resolve before initializing the next: you can do this by awaiting each of the Promises inside an async function (and you'll have to use a standard for loop to asynchronously iterate with await):
const example1 = () => new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo1');
}, 500);
});
const example2 = () => new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo2');
}, 500);
});
const doStuff = async () => {
const listExample = ['a','b','c'];
for (let i = 0; i < listExample.length; i++) {
console.log(listExample[i]);
await example1();
const s = listExample[i];
console.log("Fisrt");
await example2();
console.log("Second");
}
console.log("The End");
};
doStuff();
await is only syntax sugar for Promises - it's possible (just a lot harder to read at a glance) to re-write this without async/await:
const example1 = () => new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo1');
}, 500);
});
const example2 = () => new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo2');
}, 500);
});
const doStuff = () => {
const listExample = ['a','b','c'];
return listExample.reduce((lastPromise, item) => (
lastPromise
.then(() => console.log(item))
.then(example1)
.then(() => console.log("Fisrt"))
.then(example2)
.then(() => console.log('Second'))
), Promise.resolve())
.then(() => console.log("The End"));
};
doStuff();
If you want NOT to wait for each promise to finish before starting the next;
You can use Promise.all() to run something after all your promises have resolved;
example1 = () => new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo1');
}, 3000);
});
example2 = () => new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo2');
}, 3000);
});
doStuff = () => {
const listExample = ['a','b','c'];
let s = "";
let promises = []; // hold all the promises
listExample.forEach((item,index) => {
s = item; //moved
promises.push(example1() //add each promise to the array
.then(() => {
console.log(item); //moved
console.log("First");
}));
promises.push(example2() //add each promise to the array
.then(() => {
console.log("Second");
}));
});
Promise.all(promises) //wait for all the promises to finish (returns a promise)
.then(() => console.log("The End"));
return s;
};
doStuff();

How to execute several promises on click [duplicate]

This question already has answers here:
JS ES6 Promise Chaining
(5 answers)
Closed 4 years ago.
I'm trying to make a game using promises and call them only on mouse click (down and up), passing the state of the game from the first promise (A) to the last one (C), updating it. If promise B executes properly, promise C does not execute at all. Is it possible to chain several promises and execute them only when the event is triggered?
class A {
static draw() {
return new Promise((resolve) => {
const state = {name: 'Alex'};
resolve(state);
})
}
}
class B {
static draw(state) {
const div = document.querySelector('.app');
div.addEventListener('mousedown', () => {
return new Promise((resolve) => {
state.lastname = 'Johnson';
console.log('state with ln ' + state)
resolve(state);
})
})
}
}
class C {
static draw(state) {
const div = document.querySelector('.app');
div.addEventListener('mouseup', () => {
return new Promise((resolve) => {
state.age = '23';
console.log('state with age ' + state)
resolve(state);
})
})
}
}
A.draw()
.then(res => {
B.draw(res)
.then(res => C.draw(res))
})
Your promises are back to front. They need to be created in the scope of your draw functions (and returned by those functions) and then resolved within the callbacks, e.g.:
class B {
static draw(state) {
const div = document.querySelector('.app');
return new Promise((resolve) => {
div.addEventListener('mousedown', () => {
state.lastname = 'Johnson';
console.log('state with ln ' + state)
resolve(state);
});
});
}
}
However, such a Promise can only be resolved once, which rather begs the question as to whether promises are even the right model for what you're trying to achieve.
Heres a quick snippet on chaining promises
var firstMethod = function() {
var promise = new Promise(function(resolve, reject){
setTimeout(function() {
console.log('first method completed');
resolve({data: '123'});
}, 2000);
});
return promise;
};
var secondMethod = function(someStuff) {
var promise = new Promise(function(resolve, reject){
setTimeout(function() {
console.log('second method completed');
resolve({newData: someStuff.data + ' some more data'});
}, 2000);
});
return promise;
};
var thirdMethod = function(someStuff) {
var promise = new Promise(function(resolve, reject){
setTimeout(function() {
console.log('third method completed');
resolve({result: someStuff.newData});
}, 3000);
});
return promise;
};
firstMethod()
.then(secondMethod)
.then(thirdMethod);

Dynamic sequential execution of promises

I have a dynamic number of promises that I need to run sequentially.
I understood how I can run sequentially promises but I don't succeed to make it dynamic with a number of promises that could vary.
Here is a way I found to do it statically How to resolve promises one after another? :
function waitFor(timeout) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve(`Finished waiting ${timeout} milliseconds`);
}, timeout);
});
}
waitFor(1000).then(function(result) {
$('#result').append(result+' # '+(new Date().getSeconds())+'<br>');
return waitFor(2000);
}).then(function(result) {
$('#result').append(result+' # '+(new Date().getSeconds())+'<br>');
return waitFor(3000);
}).then(function(result) {
$('#result').append(result+' # '+(new Date().getSeconds())+'<br>');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div id="result"></div>
I would like to do the same but instead of 3 nested promises, I would like to have any number I want.
Can you help me ?
Thanks a lot!!
There are three basic ways to achieve this task with Promises.
.reduce() pattern.
function waitFor(timeout) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve(`Finished waiting ${timeout} milliseconds`);
}, timeout);
});
}
var timeouts = [1000, 2000, 2000, 3000, 1000],
sequence = tos => tos.reduce((p,c) => p.then(rp => waitFor(c))
.then(rc => console.log(`${rc} # ${new Date().getSeconds()}`)), Promise.resolve());
sequence(timeouts);
The recursive pattern.
function waitFor(timeout) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve(`Finished waiting ${timeout} milliseconds`);
}, timeout);
});
}
var timeouts = [1000, 2000, 2000, 3000, 1000],
sequence = ([to,...tos]) => to !== void 0 && waitFor(to).then(v => (console.log(`${v} # ${new Date().getSeconds()}`), sequence(tos)));
sequence(timeouts);
Scan from left pattern.
The scanl pattern would sequence promises one after another but once it is completed you also have access to the interim promise resolutions. This might be useful in some cases. If you are going to construct an asynchronous tree structure lazily (branching from the nodes only when needed) you need to have access to the previous promise resolutions.
In order to achieve scanl functionality in JS, first we have to implement it.
var scanl = (xs, f, acc) => xs.map((a => e => a = f(a,e))(acc))
we feed scanl with xs which is the array of timeouts in this particular example, f which is a callback function that takes acc (the accumulator) and e (current item) and returns the new accumulator. Accumulator values (the interim promise resolutions) are mapped over the timeouts array to be accessed when needed.
function waitFor(timeout) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve(`finished waiting ${timeout} milliseconds`);
}, timeout);
});
}
var timeouts = [1000, 2000, 2000, 3000, 1000],
scanl = (xs, f, acc) => xs.map((a => e => a = f(a,e))(acc)),
proms = scanl(timeouts, // input array
(a,t,r) => a.then(v => (r = v, waitFor(t))) // callback function
.then(v => (console.log(`${r} and ${v}`),
`${r} and ${v}`)),
Promise.resolve(`Started with 0`)); // accumulator initial value
// Accessing the previous sub sequential resolutions
Promise.all(proms)
.then(vs => vs.forEach(v => console.log(v)));
.as-console-wrapper {
max-height: 100% !important
}
Make a seprate function to handle the number of iterations
function waitFor(timeout) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve(`Finished waiting ${timeout} milliseconds`);
}, timeout);
});
}
function resultHandler(result) {
$('#result').append(result+' # '+(new Date().getSeconds())+'<br>');
return waitFor(2000);
}
function repeat(promise,num){
if(num>0)
repeat(promise.then(resultHandler),num-1);
}
repeat(waitFor(1000),2)
Forget I commented (when you convert a Promise to Observable or include the promise in an array, the Promise is executed). You can use a "recursive" function
foolPromise(index: number, interval: number) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ id: index, data: new Date().getTime() % 100000 });
}, interval);
})
}
intervals: number[] = [1000, 50, 500];
recursive(req: any, index: number) {
req.then(res => {
console.log(res);
index++;
if (index < this.intervals.length)
this.recursive(this.foolPromise(index, this.intervals[index]), index);
})
}
ngOnInit() {
this.recursive(this.foolPromise(0, this.intervals[0]), 0)
}
If you don't care for serialization, you can use Promise.all https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
Promise.all([promise1, promise2, promise3]).then(function(values) {
// do something with values
}).catch(function(err) {
// error called on first failed promise
});
Or, you can use an async function:
async function doSomething(arrayOfPromises) {
for (const item of arrayOfPromises) {
const result = await item;
// now do something with result
}
};

Why does async array map return promises, instead of values

See the code below
var arr = await [1,2,3,4,5].map(async (index) => {
return await new Promise((resolve, reject) => {
setTimeout(() => {
resolve(index);
console.log(index);
}, 1000);
});
});
console.log(arr); // <-- [Promise, Promise, Promise ....]
// i would expect it to return [1,2,3,4,5]
Quick edit:
The accepted answer is correct, by saying that map doesnt do anything special to async functions. I dont know why i assumed it recognizes async fn and knows to await the response.
I was expecting something like this, perhaps.
Array.prototype.mapAsync = async function(callback) {
arr = [];
for (var i = 0; i < this.length; i++)
arr.push(await callback(this[i], i, this));
return arr;
};
var arr = await [1,2,3,4,5].mapAsync(async (index) => {
return await new Promise((resolve, reject) => {
setTimeout(() => {
resolve(index);
console.log(index);
}, 1000);
});
});
// outputs 1, 2 ,3 ... with 1 second intervals,
// arr is [1,2,3,4,5] after 5 seconds.
Because an async function always returns a promise; and map has no concept of asynchronicity, and no special handling for promises.
But you can readily wait for the result with Promise.all:
try {
const results = await Promise.all(arr);
// Use `results`, which will be an array
} catch (e) {
// Handle error
}
Live Example:
var arr = [1,2,3,4,5].map(async (index) => {
return await new Promise((resolve, reject) => {
setTimeout(() => {
resolve(index);
console.log(index);
}, 1000);
});
});
(async() => {
try {
console.log(await Promise.all(arr));
// Use `results`, which will be an array
} catch (e) {
// Handle error
}
})();
.as-console-wrapper {
max-height: 100% !important;
}
or using Promise syntax
Promise.all(arr)
.then(results => {
// Use `results`, which will be an array
})
.catch(err => {
// Handle error
});
Live Example:
var arr = [1,2,3,4,5].map(async (index) => {
return await new Promise((resolve, reject) => {
setTimeout(() => {
resolve(index);
console.log(index);
}, 1000);
});
});
Promise.all(arr)
.then(results => {
console.log(results);
})
.catch(err => {
// Handle error
});
.as-console-wrapper {
max-height: 100% !important;
}
Side note: Since async functions always return promises, and the only thing you're awaiting in your function is a promise you create, it doesn't make sense to use an async function here anyway. Just return the promise you're creating:
var arr = [1,2,3,4,5].map((index) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(index);
console.log(index);
}, 1000);
});
});
Of course, if you're really doing something more interesting in there, with awaits on various things (rather than just on new Promise(...)), that's different. :-)
Since it is async, the values have not been determined at the time map returns. They won't exist until the arrow function has been run.
This is why Promises exist. They are a promise of a value being available in the future.

Categories