Javascript class chaning with Promise [duplicate] - javascript

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.

Related

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);

Is logging to the console the same thing that is resolved redundant?

I have a program that makes the computer simulate multiple chores at once using asynchronous JavaScript Promises. I decided to start small and focus on washing the dishes. Basically, my code has two files - a library for returning a new instance of the Promise object and an app.js which calls these functions with .then() and .catch().
This is what the functions in library.js looks like.
let washDishes = ({soap}) => {
return new Promise ((resolve, reject) => {
setTimeout(() => {
if (soap) {
console.log('Status: Clean Dishes'.blue)
resolve('clean dishes');
}
else {
console.log('Status: Dirty Dishes'.blue);
reject('dirty dishes');
}
}, 1000)
})
};
let addSoap = () => {
return new Promise ((resolve) => {
setTimeout(() => {
console.log('Status: Soap Added'.blue)
resolve('soap added');
}, 1000)
})
};
let emptyDishwasher = () => {
return new Promise ((resolve) => {
setTimeout(() => {
console.log('Status: Dishwasher Emptied'.blue)
resolve('dishes put away')
}, 1000);
})
}
In the above, I return a new promise, logging the status and resolving the same text after 1 second. Is it redundant to resolve() and console.log() at the same thing?
If I don't resolve, it won't move on. If I don't log, it doesn't show what is happening.
This is the bulk of my app.js code:
console.log("Doing chores...");
const cleanDishes = washDishes({soap: true})
.then(function(resolvedValue) {
emptyDishwasher(resolvedValue);
})
.catch(function(rejectedValue) {
addSoap(rejectedValue);
});
Putting the console.log() statements here will result in the console printing
Promise <pending>
How should I restructure my code to stop this redundancy, maintaining the fact that I am practicing Promises?
You can make your code more DRY by factoring out repeated parts in helper functions:
function withStatusLog(fn) {
return function(val) {
console.log(('Status: ' + val[0].toUpperCase() + val.slice(1)).blue);
return fn(val);
};
}
function newDelayedStatusPromise(executor) {
return new Promise((resolve, reject) => {
setTimeout(() => {
executor(withStatusLog(resolve), withStatusLog(reject))
}, 1000);
});
}
function washDishes({soap}) {
return newDelayedStatusPromise((resolveStatus, rejectStatus) => {
if (soap) {
resolveStatus('clean dishes');
} else {
rejectStatus('dirty dishes');
}
})
}
function addSoap() {
return newDelayedStatusPromise(resolveStatus => {
resolveStatus('soap added');
});
}
function emptyDishwasher() {
return newDelayedStatusPromise(resolveStatus => {
resolveStatus('dishes put away'); // or 'dishwasher emptied'?
});
}

How to dynamically add new promise to the promises chain

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);

Convert async await in while loop to promises

I can't figure out how to convert async await functionality in a while loop to a promise based implementation.
repl showing the async await version
https://repl.it/repls/IdealisticPepperyCoding
var dependency = false;
function checkDependency() {
return new Promise(resolve => {
setTimeout(() => {
dependency = true;
return resolve();
}, 1000)
});
}
async function isReady() {
while(!dependency) {
console.log('not loaded');
await checkDependency();
}
console.log('loaded')
}
isReady();
If I'm understanding you correctly, you're wanting to use Promises without async functions instead of Promises with async functions, and the sample checkDependency may actually not set dependency = true in all cases, so you want to "loop."
Equivalent functionality could look something like this, where you "recursively" call the check function. It won't actually be recursive and lead to a stack overflow, though, since the call is done in an async callback:
var dependency = false;
function checkDependency() {
return new Promise(resolve => {
setTimeout(() => {
dependency = true;
return resolve();
}, 1000)
});
}
function isReady() {
if (!dependency) {
return checkDependency().then(isReady);
}
return Promise.resolve(true);
}
isReady().then(() => {
console.log('loaded')
});
You don't need the while loop.
checkDependency().then(() => { console.log('loaded');});
You don't need to return recolve() just call it.
Call the function then within the function isReady
function checkDependency() {
return new Promise(resolve => {
setTimeout(resolve, 1000);
});
}
function isReady() {
console.log('not loaded');
checkDependency().then(() => {
console.log('loaded')
});
}
isReady();

Does promise resolved in n-th setTimeout cause memory leak?

I can see in Chrome task manager that the tab in which following code is running eats more and more memory, and it is not released until the promise is resolved
UPDATE
Main idea here is to use a single 'low level' method which would handle "busy" responses from the server. Other methods just pass url path with request data to it and awaiting for a valuable response.
Some anti-patterns was removed.
var counter = 1
// emulates post requests sent with ... axios
async function post (path, data) {
let response = (counter++ < 1000) ? { busy: true } : { balance: 3000 }
return Promise.resolve(response)
}
async function _call (path, data, resolve) {
let response = await post()
if (response.busy) {
setTimeout(() => {
_call(path, data, resolve)
}, 10)
throw new Error('busy')
}
resolve(response.balance)
}
async function makePayment (amount) {
return new Promise((resolve, reject) => {
_call('/payment/create', {amount}, resolve)
})
}
async function getBalance () {
return new Promise((resolve, reject) => {
_call('/balance', null, resolve)
})
}
makePayment(500)
.then(() => {
getBalance()
.then(balance => console.log('balance: ', balance))
.catch(e => console.error('some err: ', e))
})
The first time you call _call() in here:
async function getBalance () {
return new Promise((resolve, reject) => {
_call('/balance', null, resolve)
})
}
It will not call the resolve callback and it will return a rejected promise and thus the new Promise() you have in getBalance() will just do nothing initially. Remember, since _call is marked async, when you throw, that is caught and turned into a rejected promise.
When the timer fires, it will call resolve() and that will resolve the getBalance() promise, but it will not have a value and thus you don't get your balance. By the time you do eventually call resolve(response.balance), you've already called that resolve() function so the promise it belongs to is latched and won't change its value.
As others have said, there are all sorts of things wrong with this code (lots of anti-patterns). Here's a simplified version that works when I run it in node.js or in the snippet here in the answer:
function delay(t, val) {
return new Promise(resolve => {
setTimeout(resolve.bind(null, val), t);
});
}
var counter = 1;
function post() {
console.log(`counter = ${counter}`);
// modified counter value to 100 for demo purposes here
return (counter++ < 100) ? { busy: true } : { balance: 3000 };
}
function getBalance () {
async function _call() {
let response = post();
if (response.busy) {
// delay, then chain next call
await delay(10);
return _call();
} else {
return response.balance;
}
}
// start the whole process
return _call();
}
getBalance()
.then(balance => console.log('balance: ', balance))
.catch(e => console.error('some err: ', e))

Categories