I'm trying to implement Promise statements in my code and would love some help. I have a function that runs with a setTimeout. I want to call a function once that is completed.
Tried including Promise statements, but I have a feeling I'm not doing it correctly. Any feedback is helpful
function App(){
let exampleProm = new Promise(
function(){
type.type("hello , dexter", 100);
}
).then(console.log('finished'));
}
App();
//code that gets called first
module.exports = {
type: function(phrase, delaySpeed){
let total = 0;
let empty = [];
for(let i = 0; i < phrase.length;i++){
total += delaySpeed;
setTimeout(() => {
empty.push(phrase.charAt(i));
process.stdout.write(chalk.blue.bold(empty[i]));
if(empty.length === phrase.length){ //if complete
process.stdout.write('\n'); //puts on separate line
}
},total);
}
}
}
Use an array of promises that each resolve() inside the setTimeout() and then Promise.all() to run the code after they have all resolved
module.exports = {
type: function(phrase, delaySpeed) {
let total = 0;
let empty = [];
let promises = []
for (let i = 0; i < phrase.length; i++) {
total += delaySpeed;
// new promise for each character
let promise = new Promise(function(resolve, reject) {
setTimeout(() => {
empty.push(phrase.charAt(i));
process.stdout.write(chalk.blue.bold(empty[i]));
if (empty.length === phrase.length) { //if complete
process.stdout.write('\n'); //puts on separate line
}
// assuming above writes are synchronous can now resolve promise
resolve()
}, total);
});
// push new promise to array
promises.push(promise)
}
// return the all() promise
return Promise.all(promises)// add another then() if you need to return something to next then() in App()
}
}
function App(){
type.type("hello , dexter", 100).then(function(){
// this then() fires when the Promise.all() resolves
console.log('finished')
});
}
The reason your .then handler never gets called is because the promise is stuck in its initial state, which is "pending".
Promises can be pending or settled (fulfilled / rejected). The callback passed to a Promise constructor takes two params, resolve (trigger a fulfill) and reject (trigger a rejection).
var promise1 = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo');
}, 300);
});
promise1.then(function(value) {
console.log(value);
// expected output: "foo"
});
Check out the source for the code above at the MDN docs for the Promise API.
I reckon the example above gives you a hint on how to implement promise based delays, but if you need further assistance, let me know.
Related
I am trying to create a chain of promises where each promise waits for the previous promise before getting executed.
const syncStatusChanges = () => {
return new Promise((resolve, reject) => {
console.log("in")
setTimeout(() => {
console.log("done")
resolve({ done: true })
}, 2000);
});
}
const run = () => {
const promises = [syncStatusChanges(), syncStatusChanges()]
promises[0].then(res => {
console.log("done 1")
promises[1].then(res => {
console.log("done 2")
})
})
}
run()
In this example the output is:
in
in
done
done 1
done
done 2
But I want it to be:
in
done
done 1
in
done
done 2
I also want it to work for any n number of functions. I saw this answer but the output is the same!
var promise = statusChangeCalls[0];
for (var i = 1; i < statusChangeCalls.length; i++)
promise = promise.then(statusChangeCalls[i]);
As it is written in comments. You are executing the functions in the array itself. What i understood by seeing your output. below run function can help you.
const run = () => {
const promise = syncStatusChanges();
promise.then(res => {
console.log("done 1")
syncStatusChanges().then(res => {
console.log("done 2")
})
})
}
Promise executes eagerly. It does not wait to register the then function. You can look for observable, they are lazy in execution. Basically they wait until you subscribe them.
For your second doubt about loop. You can use async await keyword to achieve chaining. Just pass number as an parameter in runInLoop function to execute promise that many times.
const runInLoop = async(numberOfPromisesCall)=> {
for (let i = 0; i < numberOfPromisesCall; i++){
await syncStatusChanges();
console.log(`done ${i + 1}`);
}
}
runInLoop(5)
So i have an issue with JS and nodeJS, and is that it runs whole code at the same time and doesnt wait for a previous function to finish its work (compared to python). How do i make it first finish its function, push the results to array and only then print to console the whole array? await doesnt seem to work in any kind of for loop
const fetch = require('node-fetch')
const fetchlink = async (i) => {
let url = `http://linktofetch`
let response = await fetch(url, {
method: 'GET'
})
const answer = await response.json()
return answer
}
const arr = []
let pushtoarr = async (value) => {
arr.push(value)
}
let main = async () => {
for(let i=1;i < 10; i++){
const answer = fetchlink(i).then((response) => {
response.data.items.forEach(el =>{
pushtoarr(el.name)
}
)
})
}
console.log(arr)
}
main()
When doing foo.then(bar), bardoesn't execute immediately, instead you're just registering a callback that will execute bar later on, and you should instead be doing const baz = await foo; bar(baz).
So, in your example, you should rewrite your code as:
const fetch = require('node-fetch')
const fetchlink = async (i) => {
let url = `http://linktofetch`;
let response = await fetch(url, { method: 'GET' });
const answer = await response.json();
return answer;
}
(async () => {
const arr = [];
for (let i=1; i<10; i++) {
const response = await fetchLink(i);
for (const el of response.data.items) {
arr.push(el.name);
}
}
console.log(arr);
})();
Didn't test but it will look somehow like this
const fetch = require('node-fetch')
(async () => {
const arr = []
for(let i=1;i < 10; i++) {
const response = await fetchlink(i)
const answer = response.data.items.forEach(el => arr.push(el))
}
console.log(arr)
})()
async function fetchlink (i) {
let url = `http://linktofetch`
let response = await fetch(url, {
method: 'GET'
})
return response.json()
}
The problem is that you're trying to do an asynchronous task synchronously. There are generally two ways you can go about executing an async function and which one you use depends on what you need from the function.
Non-Blocking
In general, an async function will return a Promise. In order to get the results of a promise you have to unwrap it like so,
asyncFunction(args).then((promiseResult) => { doStuff(promiseResult); });
The key part is that you unwrap the promise using then which will only trigger after the original promise has finished. This means that code execution will not wait for the promise to get unwrapped to execute the lines after. For example:
asyncFunction(args).then((promiseResult) => { doStuff(promiseResult); });
console.log('done');
In this case the log function will generally happen before the doStuff function gets called.
Blocking
In the event that you want to block or wait for a promise to unwrap, you need to use the await keyword like so,
const promiseResult = await asyncFunction(args);
doStuff(promiseResult);
console.log('done');
In this example, no code after the await line will get executed until the asyncFunction resolves. The important thing to understand is that it only is true within the scope of code you are in. If there is a non-blocking async function being executed inside of asyncFunction, it will not wait to finish resolving that before returning to doStuff.
I will omit the actual modification to fix your code as it seems a few other people have beat me to that, however, I hope that explanation helps.
Use promise
example usage below
'use strict';
var promiseCount = 0;
function testPromise() {
let thisPromiseCount = ++promiseCount;
let log = document.getElementById('log');
log.insertAdjacentHTML('beforeend', thisPromiseCount +
') Started (<small>Sync code started</small>)<br/>');
// We make a new promise: we promise a numeric count of this promise, starting from 1 (after waiting 3s)
let p1 = new Promise(
// The executor function is called with the ability to resolve or
// reject the promise
(resolve, reject) => {
log.insertAdjacentHTML('beforeend', thisPromiseCount +
') Promise started (<small>Async code started</small>)<br/>');
// This is only an example to create asynchronism
window.setTimeout(
function() {
// We fulfill the promise !
resolve(thisPromiseCount);
}, Math.random() * 2000 + 1000);
}
);
// We define what to do when the promise is resolved with the then() call,
// and what to do when the promise is rejected with the catch() call
p1.then(
// Log the fulfillment value
function(val) {
log.insertAdjacentHTML('beforeend', val +
') Promise fulfilled (<small>Async code terminated</small>)<br/>');
}).catch(
// Log the rejection reason
(reason) => {
console.log('Handle rejected promise ('+reason+') here.');
});
log.insertAdjacentHTML('beforeend', thisPromiseCount +
') Promise made (<small>Sync code terminated</small>)<br/>');
}
reference:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
In JavaScript, I have an array of objects being some tasks to do. I iterate through this array with a for loop with await, calling a function doOneTask with returns a Promise.
That works pretty well as long as the code inside doOneTask works as expected. However, those things often fail. Trying again helps almost all the time. So, I'd like to implement a procedure for auto-retrying inside the JavaScript code.
My idea was a recursive function: In case of a failure, doOneTask call itself till the promise if finally resolved.
My code looks like this:
var tasks = [{label: 'task0'},{label: 'task1'},{label: 'task2'}];
async function mainFunction() {
for(let k = 0; k < tasks.length; k++) {
await doOneTask(tasks[k]);
console.log("doOneTask done for index " + k);
}
console.log("End reached!");
}
function doOneTask(task) {
return new Promise(async function (resolve,reject) {
console.log("Starting with: " + task.label);
let checkIfDoeSomeStuffWorked = await doSomeAsyncStuff();
if(checkIfDoeSomeStuffWorked == false) {
console.log(task.label + ": FAILED");
return doOneTask(task);
}
else {
console.log(task.label + ": SUCCESS");
resolve(true);
}
});
}
function doSomeAsyncStuff() {
return new Promise(function (resolve,reject) {
var myRandom = Math.random();
if(myRandom < 0.3) {
resolve(true);
}
else {
resolve(false);
}
});
}
mainFunction();
(In real life, doSomeAsyncStuff is a backend call which often fails. The random() part is just for demonstration. In reality, I also limit the number of trials, before stopping the script.)
However, it doesn't work. In case of a failure, the script stops after having reached the SUCCESS console log. I never get back to the loop and the next items in the loop never get executed.
You have no use for the q library dependency. async functions always return a Promise, so you can simplify your code quite a bit -
async function doOneTask (task) {
const result = await doSomeAsyncStuff()
if (result === false) {
console.log(`${task} failed`)
return doOneTask(task)
}
else {
console.log(`${task} passed`)
return true
}
}
Your fake function doSomeAsyncStuff can be cleaned up too -
async function doSomeAsyncStuff () {
return Math.random() < 0.3
}
But let's add a fake delay of 1 second so that we can show things working 100% -
async function doSomeAsyncStuff () {
return new Promise(resolve =>
setTimeout(resolve, 1000, Math.random() < 0.3)
)
}
Last, your main function uses a really old looping convention. As you're using modern JavaScript, you might as well use for-of syntax -
async function main (tasks = []) {
for (const t of tasks) {
await doOneTask(t)
}
return "done"
}
Finally we run the program -
const tasks =
[ 'task0', 'task1', 'task2' ]
main(tasks).then(console.log, console.error)
// task0 failed
// task0 passed
// task1 failed
// task1 failed
// task1 passed
// task2 passed
// done
Expand the snippet below to verify the results in your own browser -
async function doOneTask (task) {
const result = await doSomeAsyncStuff()
if (result === false) {
console.log(`${task} failed`)
return doOneTask(task)
}
else {
console.log(`${task} passed`)
return true
}
}
async function doSomeAsyncStuff () {
return new Promise(resolve =>
setTimeout(resolve, 1000, Math.random() < 0.3)
)
}
async function main (tasks = []) {
for (const t of tasks) {
await doOneTask(t)
}
return "done"
}
const tasks =
[ 'task0', 'task1', 'task2' ]
main(tasks).then(console.log, console.error)
// task0 failed
// task0 passed
// task1 failed
// task1 failed
// task1 passed
// task2 passed
// done
After having completed the question, but just before posting, something came into my mind: In the setup above, I don't resolve the very same promise object when I finally reach success, but for each function call a new promise object is generated. My solution/workaround is quite simple: use q the promise library passing the promise from one function call to the next function call:
var q = require('q');
async function doOneTask(task,promiseObj) {
if(!promiseObj) {
var promiseObj = q.defer();
}
console.log("Starting with: " + task.label);
let checkIfDoeSomeStuffWorked = await doSomeAsyncStuff();
if(checkIfDoeSomeStuffWorked == false) {
console.log(task.label + ": FAILED");
return doOneTask(task,promiseObj);
}
else {
console.log(task.label + ": SUCCESS");
promiseObj.resolve(true);
}
return promiseObj.promise;
}
That way, we make sure that the very same promise object which is generated at the first call of doOneTask is resolved in the end - event after the 20th execution.
I have two HTTP calls on a page and they are separate altogether.
vm.$onInit = function() {
....
....
//Get all the items only once during initialization
ds.getAllItems().then(function(result){
vm.items = result;
},function(error){
vm.errorInApi = true;
});
.....
.....
}
vm.getTimes = function(){
.....
.....
HttpWrapper.send(url,{"operation":'GET'}).then(function(times){
.....
}
If both the APIs fail then only I need to show a modal.
I can initiate a variable to true and on failure of the APIs, I can make that false and then only show the modal.
But then how long to wait for completion of all the APIs?
Hmm... simply invert the polarity of the promises and use Promise.all().
Promise.all() would normally resolve once all promises resolve, so once the promises are inverted, it resolves once all promises get rejected...
var invert = p => new Promise((v, x) => p.then(x, v));
Promise.all([Promise.reject("Error 404"), Promise.reject("Error WTF")].map(invert))
.then(v => console.log(v));
So as per #guest271314 comment i extend the solution in a silver spoon to show how inverting promises can be applied for this task.
var invert = p => new Promise((v, x) => p.then(x, v)),
prps = [Promise.reject("Error 404"), Promise.reject("Error WTF")]; // previously rejected promises
myButton.addEventListener('click', function(e){
setTimeout(function(...p){
p.push(Promise.reject("Error in Click Event Listener"));
Promise.all(p.map(invert))
.then(r => results.textContent = r.reduce((r,nr) => r + " - " + nr));
}, 200, ...prps);
});
<button id="myButton">Check</button>
<p id="results"></p>
If any of the promises including the previously obtained ones or the once in the event handler gets resolved you will get no output.
You can use async/await and .catch() to determine the number of rejected Promises, perform action if the number is equal to N, where N is the number of rejected Promise values required to perform action.
A prerequisite, as mentioned by #trincot, is to return the Promise from the function and return a value from the function passed to .then(), and throw an Error() from .catch() or function at second parameter of .then() see Why is value undefined at .then() chained to Promise?
const N = 2;
function onInit() {
return Promise.resolve("resolved")
}
function getTimes() {
return Promise.reject("rejected");
}
const first = onInit();
document.querySelector("button")
.onclick = async function() {
let n = 0;
const firstP = await first.catch(() => ++n);
const secondP = await getTimes().catch(() => ++n);
if (n === N) {
// do stuff if `n` is equal to `N`
} else {
// do other stuff
console.log(n, N)
}
};
<button>click</button>
You could use Promise.all.
First you should get the two promises in an array. To achieve that, it is probably useful if your two functions return the promise they create:
vm.$onInit = function() {
....
....
return ds.getAllItems().then(function(result){
vm.items = result;
},function(error){
vm.errorInApi = true;
});
}
vm.getTimes = function(){
.....
.....
return HttpWrapper.send(url,{"operation":'GET'}).then(function(times){
.....
});
}
The array would then be built from the return values:
var arr = [];
arr.push(vm.$onInit());
...
arr.push(vm.getTimes());
You write in a comment that getTimes "is called on some button click", so you would do that second push there.
Or, maybe you see another way to get these promises in an array... it does not matter much how you do it, as long as you achieve this.
Then (in that click handler) you need to detect the situation where both the promises are rejected. Promise.all can do that, but you need to flip the promise results:
// Flip promises so that a rejected one is regarded as fulfilled and vice versa:
arr = arr.map(p => p.then(res => { throw res }).catch(err => err));
// Detect that all original promises rejected, i.e. that the new promises all fulfill.
Promise.all(arr).then(function() {
// execute whatever you need to execute when both ajax calls failed
}).catch(err => err); // ignore
I'm tackling a project that requires me to use JavaScript with an API method call. I'm a Java programmer who has never done web development before so I'm having some trouble with it.
This API method is asynchronous and it's in a while loop. If it returns an empty array, the while loop finishes. Otherwise, it loops. Code:
var done = true;
do
{
async_api_call(
"method.name",
{
// Do stuff.
},
function(result)
{
if(result.error())
{
console.error(result.error());
}
else
{
// Sets the boolean to true if the returned array is empty, or false otherwise.
done = (result.data().length === 0) ? true : false;
}
}
);
} while (!done);
This doesn't work. The loop ends before the value of "done" is updated. I've done some reading up on the subject and it appears I need to use promises or callbacks because the API call is asynchronous, but I can't understand how to apply them to the code I have above.
Help would be appreciated!
edit: see the bottom, there is the real answer.
I encourage you yo use the Promise API. Your problem can be solved using a Promise.all call:
let promises = [];
while(something){
promises.push(new Promise((r, j) => {
YourAsyncCall(() => r());
});
}
//Then this returns a promise that will resolve when ALL are so.
Promise.all(promises).then(() => {
//All operations done
});
The syntax is in es6, here is the es5 equivalent (Promise API may be included externally):
var promises = [];
while(something){
promises.push(new Promise(function(r, j){
YourAsyncCall(function(){ r(); });
});
}
//Then this returns a promise that will resolve when ALL are so.
Promise.all(promises).then(function(){
//All operations done
});
You can also make your api call return the promise and push it directly to the promise array.
If you don't want to edit the api_call_method you can always wrap your code in a new promise and call the method resolve when it finishes.
edit: I have seen now the point of your code, sorry. I've just realized that Promise.all will not solve the problem.
You shall put what you posted (excluding the while loop and the control value) inside a function, and depending on the condition calling it again.
Then, all can be wraped inside a promise in order to make the external code aware of this asynchronous execution. I'll post some sample code later with my PC.
So the good answer
You can use a promise to control the flow of your application and use recursion instead of the while loop:
function asyncOp(resolve, reject) {
//If you're using NodeJS you can use Es6 syntax:
async_api_call("method.name", {}, (result) => {
if(result.error()) {
console.error(result.error());
reject(result.error()); //You can reject the promise, this is optional.
} else {
//If your operation succeeds, resolve the promise and don't call again.
if (result.data().length === 0) {
asyncOp(resolve); //Try again
} else {
resolve(result); //Resolve the promise, pass the result.
}
}
});
}
new Promise((r, j) => {
asyncOp(r, j);
}).then((result) => {
//This will call if your algorithm succeeds!
});
/*
* Please note that "(...) => {}" equivals to "function(...){}"
*/
sigmasoldier's solution is correct, just wanted to share the ES6 version with async / await:
const asyncFunction = (t) => new Promise(resolve => setTimeout(resolve, t));
const getData = async (resolve, reject, count) => {
console.log('waiting');
await asyncFunction(3000);
console.log('finshed waiting');
count++;
if (count < 2) {
getData(resolve, reject, count);
} else {
return resolve();
}
}
const runScript = async () => {
await new Promise((r, j) => getData(r, j, 0));
console.log('finished');
};
runScript();
If you don't want to use recursion you can change your while loop into a for of loop and use a generator function for maintaining done state. Here's a simple example where the for of loop will wait for the async function until we've had 5 iterations and then done is flipped to true. You should be able to update this concept to set your done variable to true when your webservice calls have buffered all of your data rows.
let done = false;
let count = 0;
const whileGenerator = function* () {
while (!done) {
yield count;
}
};
const asyncFunction = async function(){
await new Promise(resolve => { setTimeout(resolve); });
};
const main = new Promise(async (resolve)=>{
for (let i of whileGenerator()){
console.log(i);
await asyncFunction();
count++;
if (count === 5){
done = true;
}
}
resolve();
});
main.then(()=>{
console.log('all done!');
});
Also you may try recursion solution.
function asyncCall(cb) {
// Some async operation
}
function responseHandler(result) {
if (result.error()) {
console.error(result.error());
} else if(result.data() && result.data().length) {
asyncCall(responseHandler);
}
}
asyncCall(responseHandler);
Here is a solution I came up with. Place this in an async function.
let finished = false;
const loop = async () => {
return new Promise(async (resolve, reject) => {
const inner = async () => {
if (!finished) {
//insert loop code here
if (xxx is done) { //insert this in your loop code after task is complete
finshed = true;
resolve();
} else {
return inner();
}
}
}
await inner();
})
}
await loop();
If you don't want to use Promises you can restructure your code like so:
var tasks = [];
var index = 0;
function processNextTask()
{
if(++index == tasks.length)
{
// no more tasks
return;
}
async_api_call(
"method.name",
{
// Do stuff.
},
function(result)
{
if(result.error())
{
console.error(result.error());
}
else
{
// process data
setTimeout(processNextTask);
}
}
);
}
Your loop won't work, because it is sync, your async task is async, so the loop will finish before the async task can even respond. I'd reccomend you to use Promises to manage async tasks:
//first wrapping your API into a promise
var async_api_call_promise = function(methodName, someObject){
return new Promise((resolve, reject) => {
async_api_call(methodName, someObject, function(result){
if(result.error()){
reject( result.error() )
}else{
resolve( result.data() )
}
});
})
}
now to your polling code:
//a local utility because I don't want to repeat myself
var poll = () => async_api_call_promise("method.name", {/*Do stuff.*/});
//your pulling operation
poll().then(
data => data.length === 0 || poll(), //true || tryAgain
err => {
console.error(err);
return poll();
}
).then((done) => {
//done === true
//here you put the code that has to wait for your "loop" to finish
});
Why Promises? Because they do state-management of async operations. Why implement that yourself?
let taskPool = new Promise(function(resolve, reject) {
resolve("Success!");
});
let that = this;
while (index < this.totalPieces) {
end = start + thisPartSize;
if (end > filesize) {
end = filesize;
thisPartSize = filesize - start;
}
taskPool.then(() => {
that.worker(start, end, index, thisPartSize);
});
index++;
start = end;
}