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'?
});
}
Related
I have a similar problem in dialogflow fulfillment where I need to book any appointments on the Google calendar. I would reject all functions p1, p2 and p3 if only one of them is rejected. In the code below, although p2 is rejected, the others p1 and p3 are executed (I wish all functions p1, p2 and p3 were not performed).
function f1() {
return Promise.all([p1(), p2(), p3()])
.then(value => {
alert('ok');
})
.catch(err => {
console.log('err: ' + err);
});
}
function p1() {
new Promise((resolve, reject) => {
setTimeout(resolve, 1000, alert("one"));
});
}
function p2() {
new Promise((resolve, reject) => {
reject('reject');
});
}
function p3() {
new Promise((resolve, reject) => {
setTimeout(resolve, 3000, alert("three"));
});
}
f1();
Well it contains a lot of code to implement it so I would give short instructions.
You need to have a way to reject your request or whatever you are doing. For examples with axious we can use CancelToken to cancel HTTP request.
Now you need to subscribe on each request and cancel the request or whatever you are using.
It is not clear what exactly you need. Functions can not be rejected or not executed if you run them in parallel. You can only notify the internal function code that the cancel operation was requested from the outside. We don't know what async operations are performed inside your async functions. Demo
import { CPromise } from "c-promise2";
function makePromise(ms, label) {
return new CPromise((resolve, reject, { onCancel }) => {
setTimeout(resolve, ms);
onCancel(() => console.log(`onCancel(${label})`));
});
}
CPromise.all([
makePromise(2000, "one"),
makePromise(1500, "two").then(() => {
throw Error("Oops");
}),
makePromise(4000, "three")
]).then(
(v) => console.log(`Done: ${v}`),
(e) => console.warn(`Fail: ${e}`)
);
onCancel(one)
onCancel(three)
Fail: Error: Oops
The problem is you're not returning anything from p1, p2, p3. So when you call Promise.all([p1(), p2(), p3()]) which is actually calling with Promise.all([undefined, undefined, undefined])(resolves anyway) which does not have a rejection. That's why you're not seeing the error.
Add return in your functions.
function f1() {
return Promise.all([p1(), p2(), p3()])
.then(value => {
alert('ok');
})
.catch(err => {
console.log('err: ' + err);
});
}
function p1() {
return new Promise((resolve, reject) => {
setTimeout(resolve, 1000);
});
}
function p2() {
return new Promise((resolve, reject) => {
reject('reject');
});
}
function p3() {
return new Promise((resolve, reject) => {
setTimeout(resolve, 3000);
});
}
f1();
Remember Promises are not cancellable, if you really want cancel execution part, you can try something like this. I don't guarantee this works all the time and I don't think good idea to do in this way.
const timers = []
function f1() {
return Promise.all([p1(), p2(), p3()])
.then(value => {
alert('ok');
})
.catch(err => {
console.log('err: ' + err);
timers.forEach((timerId) => clearTimeout(timerId))
});
}
function p1() {
return new Promise((resolve, reject) => {
const timerId = setTimeout(() => {
alert(1)
resolve()
}, 1000);
timers.push(timerId)
});
}
function p2() {
return new Promise((resolve, reject) => {
reject('reject');
});
}
function p3() {
return new Promise((resolve, reject) => {
const timerId = setTimeout(() => {
alert(2)
resolve()
}, 3000);
timers.push(timerId)
});
}
f1();
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.
I was searching online on how we create the traditional way of doing async function in Javascript but it wasn't available. I have implemented a promise function in my program, however the software that I am using (tableu) to create all custom styling does not support ES5-ES8 and async functions, as this will throw an error, so I was wondering if this is possible.
function promise() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(), 500);
})
}
async function result() {
await promise();
}
result().then(render => {
customStyle()
});
All of my code shown is working fine. I'm wondering how can I convert this to the old way of doing async functions. Is this possible or is it only available in ES8?
Callbacks are the non-Promise or async/await way of doing this, and are basically how those things work under the hood.
Here's a simple example by modifying your snippet:
function promise(callback) {
setTimeout(() => callback(), 500);
}
function result() {
promise(callback);
}
function callback() {
customStyle();
};
Instead of result being an async function that you can await elsewhere in your code, it could also take a callback argument, like promise, that you would pass it when it's invoked. The implementation of that callback function would be like the then of an actual Promise.
Now you can see why the Promise API and async/await were such nice improvements to the spec.
To use promises the tradicional way, you have to replace the await and use .then(()=> ...). I'll try to show a snippet here to help you to understood.
The code that you have shown does not need the async or await, it goes well like that
function promise() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('resolved')
resolve()
}, 500);
})
}
promise().then(render => {
customStyle()
});
Here i'll show you a code that have a good use of it and then I'll convert it:
function callSomeService() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('service whas called')
resolve({ idOfSomething: 1 })
}, 2000);
})
}
function callAnotherServiceUsingTheDataOfThePreviousCall(data) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('service whas called with', data)
resolve(['potato 1', 'potato 2', 'potato 3'])
}, 2000);
})
}
async function result() {
const serviceResponse = await callSomeService();
const arrayOfPotatos = await callAnotherServiceUsingTheDataOfThePreviousCall(serviceResponse);
return arrayOfPotatos.map((potato, index) => `${index} - ${potato}`)
}
result().then(arrayOfPotatos => {
arrayOfPotatos.forEach(potato => console.log(potato))
});
Now I'll convert it to not use async or await, but still using promises.
function callSomeService() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('service whas called')
resolve({ idOfSomething: 1 })
}, 2000)
})
}
function callAnotherServiceUsingTheDataOfThePreviousCall(data) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('service whas called with', data)
resolve(['potato 1', 'potato 2', 'potato 3'])
}, 2000)
})
}
function result() {
return callSomeService().then(serviceResponse => {
return callAnotherServiceUsingTheDataOfThePreviousCall(
serviceResponse
).then(arrayOfPotatos => {
return arrayOfPotatos.map((potato, index) => `${index} - ${potato}`)
})
})
}
result().then(arrayOfPotatos => {
arrayOfPotatos.forEach(potato => console.log(potato))
})
Those two last codes does the same thing, but the second use async and await and the third does not. Async and await are just a syntax sugar to use promises.
I expect that will help you.
I've been fighting with updating the state in Reactjs in the correct order for the past few days which made me realise I need to handle my asynchronous functions properly. Unfortunately, it turns out I don't fully understand Promise() either. I am struggling to make the Promise chain work correctly as my third function is never called in the example below.
componentDidMount() {
this.mockOne()
.then(this.mockTwo())
.then((successMessage) => {
console.log('successMessage: ', successMessage);
this.mockThree()
});
}
mockOne() {
return new Promise((resolve, reject) => {
console.log('mockOne')
})
}
mockTwo() {
return new Promise((resolve, reject) => {
setTimeout(function() {
console.log('mockTwo')
}, 2000)
})
.catch(err => console.log('There was an error in mockTwo:' + err));
}
mockThree() {
return new Promise((resolve, reject) => {
console.log('mockThree')
})
}
console output
I've tried the instructions in the MDC but either mockThree() is called immediately before mockTwo() has a chance to respond or mockThree() isn't called at all.
Any help to get this working will be greatly appreciated.
The answer provided worked perfectly until I tried to chain a few more asynchronous functions. Can anyone help me understand why my first function causes the workflow to pause but the next three functions are completed immediately please?
componentDidMount() {
this.mockOne()
.then(successMessage => {
this.mockTwo();
})
.then(successMessage => {
this.mockThree();
})
.then(successMessage => {
this.mockFour();
});
}
mockOne() {
return new Promise((resolve, reject) => {
console.log("mockOne");
setTimeout(function() {
resolve("Test success message");
}, 2000);
}).catch(err => console.log("There was an error in mockOne:" + err));
}
mockTwo() {
return new Promise((resolve, reject) => {
console.log("mockTwo");
setTimeout(function() {
resolve("Test success message");
}, 2000);
}).catch(err => console.log("There was an error in mockTwo:" + err));
}
mockThree() {
return new Promise((resolve, reject) => {
console.log("mockThree");
setTimeout(function() {
resolve("Test success message");
}, 2000);
}).catch(err => console.log("There was an error in mockThree:" + err));
}
mockFour() {
return new Promise((resolve, reject) => {
console.log("mockFour");
setTimeout(function() {
resolve("Test success message");
}, 2000);
}).catch(err => console.log("There was an error in mockFour:" + err));
}
You have to call the resolve function for the promise to become fulfilled. You also need to make sure you are not invoking this.mockTwo() straight away, but instead just give the function this.mockTwo to then.
class App extends React.Component {
componentDidMount() {
this.mockOne()
.then(this.mockTwo)
.then(successMessage => {
console.log("successMessage: ", successMessage);
this.mockThree();
});
}
mockOne() {
return new Promise((resolve, reject) => {
console.log("mockOne");
resolve();
});
}
mockTwo() {
return new Promise((resolve, reject) => {
console.log("mockTwo");
setTimeout(function() {
resolve("Test success message");
}, 2000);
}).catch(err => console.log("There was an error in mockTwo:" + err));
}
mockThree() {
return new Promise((resolve, reject) => {
console.log("mockThree");
resolve();
});
}
render() {
return null;
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
I figured out the answer to my expanded (edited) question of why the chain wasn't working as expected. All I had to do was chain the functions properly, as below:
this.mockOne()
.then(successMessage => {
this.mockTwo()
.then(successMessage => {
this.mockThree()
.then(successMessage => {
this.mockFour()
})
})
})
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))