how to break for loop in promise? - javascript

var ing_data = savedata.ingredients.split(',');
for(var i =0; i<ing_data.length; i++){
var d = {
content_name: ing_data[i],
dogFoodId: dogId
}
db.dog_ingredients.create(d).then(function(data){
}, function(e){
console.log(e);
res.status(403).send('Error');
//break for loop this point
});
}
how to break for loop in promise?
I'm using node express, sequelize module

The loop will be over before the first then callback is triggered; this is one of the guarantees of promises (assuming that create operation returns a proper promise, not just a thenable; or at least that the thenable it returns completes asynchronously).
You can use the reduce trick to loop through adding those ingredients serially (one at a time); a promise rejection along the way will skip the remaining ingredients:
savedata.ingredients.split(',').reduce(function(p, ing) {
// Chain this ingredient on the end of the promise, return
// the new promise `then` returns, which gets passed to the
// next iteration
return p.then(function() {
var d = {
content_name: ing,
dogFoodId: dogId
};
// Return the promise from `create`
return db.dog_ingredients.create(d);
});
}, Promise.resolve()/* Seeds the loop above */)
.catch(function(e) {
// We got a rejection, which bypasses any pending resolution
// handlers we set up above; process the rejection.
console.log(e);
res.status(403).send('Error');
return Promise.reject(e); // Only need to propgate the rejection like this
// this if something will use the return value of
// this overall structure
});
That looks massive, but that's mostly comments and the object initializer; we could also write it like this (assuming we didn't need to propagate the rejection):
savedata.ingredients.split(',').reduce(function(p, ing) {
return p.then(function() {
return db.dog_ingredients.create({ content_name: ing, dogFoodId: dogId });
});
}, Promise.resolve())
.catch(function(e) {
res.status(403).send('Error');
});
(Or you can even get smaller, but for me debugging suffers — leave minifying to the minifier.)
I assume you don't want to add the ingredients in parallel since you've indicated you want to stop on the "first" error. But if you did, the code would be simpler:
Promise.all(savedata.ingredients.split(',').map(function(ing) {
return db.dog_ingredients.create({ content_name: ing, dogFoodId: dogId });
}).catch(function(e) {
res.status(403).send('Error');
return Promise.reject(e);
});
(Assumes we don't need to propagate the rejection.)
Again, though, that's parallel.

Related

JavaScript: execution sequence inside a function

I have a spinner element in my view which is corresponding with vm.wuDataLoading. Spinner is displayed while vm.wuDataLoading is true. I want to stop displaying spinner when data is loaded using reportDataService.getStandardReportGridData . Is this a correct way to do it or I need to somehow ensure that vm.wuDataLoading is not changed to false before the data is fully loaded?
function changeTitle(title) {
/////some code
function getData() {
vm.wuDataLoading = true;
reportDataService.getStandardReportGridData(projectId, webreportIdWuSet).then(function(data3) {
vm.setRowWu01 = (formatNumber(data3.Values[0][1]));
vm.setRowWu02 = (formatNumber(data3.Values[0][2]));
});
////some code
vm.wuDataLoading = false;
});
getData();
};
Presumably reportDataService.getStandardReportGridData returns a promise for a reason: It's asynchronous. So naturally, you don't want to clear the loading flag until that asynchronous work is complete (e.g., within a then callback). Something like this:
function getData() {
vm.wuDataLoading = true;
reportDataService.getStandardReportGridData(projectId, webreportIdWuSet).then(function(data3) {
vm.setRowWu01 = (formatNumber(data3.Values[0][1]));
vm.setRowWu02 = (formatNumber(data3.Values[0][2]));
})
// "finally"
.catch(e => {
// Do something about the fact the promise was rejected, but don't throw
// and don't return a rejected promise
})
.then(() => {
// Because our `catch` above converts the rejection to resolution, this gets
// called regardless of whether the original promise rejects or resolves
vm.wuDataLoading = false;
});
////some code <== This may or may not need to be in a callback above, depending on whether
// you want it to run before or after the work is complete
});
or I need to somehow ensure that vm.wuDataLoading is not changed to false before the data is fully loaded
If other things are also going to use the same flag, I'd suggest using a counter instead:
function getData() {
// Increment the counter, where >0 means "loading"
++vm.wuDataLoading;
reportDataService.getStandardReportGridData(projectId, webreportIdWuSet).then(function(data3) {
vm.setRowWu01 = (formatNumber(data3.Values[0][1]));
vm.setRowWu02 = (formatNumber(data3.Values[0][2]));
})
// "finally"
.catch(e => {
// Do something about the fact the promise was rejected, but don't throw
// and don't return a rejected promise
})
.then(() => {
// Because our `catch` above converts the rejection to resolution, this gets
// called regardless of whether the original promise rejects or resolves
// Decrement the counter again
--vm.wuDataLoading;
});
////some code <== This may or may not need to be in a callback above, depending on whether
// you want it to run before or after the work is complete
});
Note that you would only use the pattern above if, as in getData, you weren't going to pass on the promise to the calling code. If you did (by returning the result of calling then), it would be inappropriate to convert rejection to resolution via a catch handler like that.
In that case, isolate any code you want to run when the promise settles (regardless of how) in a local function, and use it both in your then handler and a catch handler that propagates the error:
function getData() {
function cleanup() {
// Decrement the counter
--vm.wuDataLoading;
}
// Increment the counter, where >0 means "loading"
++vm.wuDataLoading;
return reportDataService.getStandardReportGridData(projectId, webreportIdWuSet).then(function(data3) {
vm.setRowWu01 = (formatNumber(data3.Values[0][1]));
vm.setRowWu02 = (formatNumber(data3.Values[0][2]));
cleanup();
})
.catch(e => {
cleanup();
throw e;
});
});

Why do JavaScript promises chain rejections into resolved promises?

I'm wondering why rejections are chained as resolved with ES6 promises?
Here's an example:
let p = Promise.reject().then(function () {
return 'one';
}, function() {
return 'two';
});
p.then(function (value) {
// I do not expect this to be executed, since it was not resolved.
console.log(value);
});
The above outputs "two" to the console.
https://jsfiddle.net/pscb88xg/1/
Why does the chaining of a promise mutate a rejection into a successful resolve?
Edit: I want to clarify that the question has practical application.
What if you want to convert data from A to B using chaining.
p.then(function (A) {
return new B(A);
});
The above mutates rejections into resolved values. Even if no reject callback is used.
For example;
let p = Promise.reject('error').then(function (A) {
return new B(A);
});
// some code elsewhere
p.then(function (B) {
console.log('success!');
});
In the above example. The value B is not B but the error, and it was resolved successfully later in the chain.
Is this normal?
Edit: I understand my confusion now. I was extracting HTTP header values in rejections like this.
let p = myHttpService.get(...).then(function() {
//....
}, function(response) {
// get headers
});
The above was chaining my promises to a resolved value, and I didn't understand why. I can fix my code with the following.
let p = myHttpService.get(...).then(function() {
//....
}, function(response) {
// get headers
return Promise.reject(response);
});
After handling an error you usually want your code to continue, similar to how code after a catch block runs like normal, whereas uncaught exceptions abort.
If you want to abort instead then don't handle the error until the end of the chain:
let p = Promise.reject().then(function () {
return 'one';
});
p.then(function (value) {
// This won't run, because the rejection hasn't been handled yet
console.log(value);
}, function() {
return console.log( 'there was a problem' );
}).then(function ( ) {
// This will run because the rejection has been dealt with already.
console.log( 'Moving on');
});
MDN documentation for Promise.prototype.then says:
After the invocation of the handler function [the function passed to then()], the promise returned by then gets resolved with the returned value as its value.
It's meant to allow you to gracefully recover from an error in a promise chain.
An example might be the 304 Not Modified response from the server. If you were to use a promise based library to do an http request any response that's not 2XX will be considered a failure and the promise will be rejected. From an application's point of view however 304 might just as good as a 200 and you'd like to continue as normal.
This is the same behavior as AngularJS's $q provider.
The mutation occurs because in your rejection handler, you are returning a value and not a rejected promise. If you were to instead, pass a rejected promise, it would behave how you were expecting:
let p = Promise.reject().then(function () {
return 'one';
}, function() {
return Promise.reject('two');
});
p.then(function (value) {
// I do not expect this to be executed, since it was not resolved.
console.log(value);
}, function() {
console.log("Rejected, baby!");
});

serializes ajax calls without interrupting by failure

I need to make a list of ajax calls execute sequentially. In other words,
one starts after another finishes.
I implement this by chaining jQuery promise. And I need the calls executed no matter the former ones succeeded or not. So I wrap the promise returned by jquery to a new one. It works. But I wonder if promise could support this case directly without using the wrap.
function getOne(apple) {
return $.get(apple)
.done(function() {
console.info("I'm done " + apple);
})
.fail(function() {
console.info("I fail " + apple);
});
};
function getAll(apples) {
return _.reduce(apples, function(result, apple) {
return result.then(function() {
var d = $.Deferred();
getOne(apple).always(function() {
d.resolve();
});
return d.promise();
})
}, $.when(1));
};
i hope this pseudo code helps:
var sequence = Promise.resolve();
for(loop){
sequence = sequence.then(function(url){
return ajaxGet(url);
}).catch(function(err){
console.log(err);
})
}
the idea is that you chain promises one after another, and the next one starts only when previous resolves or rejects.

Proper while() loop for bluebird promises (without recursion?)

I've been learning promises using bluebird for two weeks now. I have them mostly understood, but I went to go solve a few related problems and it seems my knowledge has fell apart. I'm trying to do this simple code:
var someGlobal = true;
whilePromsie(function() {
return someGlobal;
}, function(result) { // possibly even use return value of 1st parm?
// keep running this promise code
return new Promise(....).then(....);
});
as a concrete example:
// This is some very contrived functionality, but let's pretend this is
// doing something external: ajax call, db call, filesystem call, etc.
// Simply return a number between 0-999 after a 0-999 millisecond
// fake delay.
function getNextItem() {
return new Promise.delay(Math.random()*1000).then(function() {
Promise.cast(Math.floor(Math.random() * 1000));
});
}
promiseWhile(function() {
// this will never return false in my example so run forever
return getNextItem() !== false;
}, // how to have result == return value of getNextItem()?
function(result) {
result.then(function(x) {
// do some work ...
}).catch(function(err) {
console.warn("A nasty error occured!: ", err);
});
}).then(function(result) {
console.log("The while finally ended!");
});
Now I've done my homework! There is the same question, but geared toward Q.js here:
Correct way to write loops for promise.
But the accepted answers, as well as additional answers:
Are geared toward Q.js or RSVP
The only answer geared toward bluebird uses recursion. These seems like it's likely to cause a huge stack overflow in an infinite loop such as mine? Or at best, be very inefficient and create a very large stack for nothing? If I'm wrong, then fine! Let me know.
Don't allow you to use result of the condition. Although this isn't requirement -- I'm just curious if it's possible. The code I'm writing, one use case needs it, the other doesn't.
Now, there is an answer regarding RSVP that uses this async() method. And what really confuses me is bluebird documents and I even see code for a Promise.async() call in the repository, but I don't see it in my latest copy of bluebird. Is it in the git repository only or something?
It's not 100% clear what you're trying to do, but I'll write an answer that does the following things you mention:
Loops until some condition in your code is met
Allows you to use a delay between loop iterations
Allows you to get and process the final result
Works with Bluebird (I'll code to the ES6 promise standard which will work with Bluebird or native promises)
Does not have stack build-up
First, let's assume you have some async function that returns a promise whose result is used to determine whether to continue looping or not.
function getNextItem() {
return new Promise.delay(Math.random()*1000).then(function() {
return(Math.floor(Math.random() * 1000));
});
}
Now, you want to loop until the value returned meets some condition
function processLoop(delay) {
return new Promise(function(resolve, reject) {
var results = [];
function next() {
getNextItem().then(function(val) {
// add to result array
results.push(val);
if (val < 100) {
// found a val < 100, so be done with the loop
resolve(results);
} else {
// run another iteration of the loop after delay
setTimeout(next, delay);
}
}, reject);
}
// start first iteration of the loop
next();
});
}
processLoop(100).then(function(results) {
// process results here
}, function(err) {
// error here
});
If you wanted to make this more generic so you could pass in the function and comparison, you could do this:
function processLoop(mainFn, compareFn, delay) {
return new Promise(function(resolve, reject) {
var results = [];
function next() {
mainFn().then(function(val) {
// add to result array
results.push(val);
if (compareFn(val))
// found a val < 100, so be done with the loop
resolve(results);
} else {
// run another iteration of the loop after delay
if (delay) {
setTimeout(next, delay);
} else {
next();
}
}
}, reject);
}
// start first iteration of the loop
next();
});
}
processLoop(getNextItem, function(val) {
return val < 100;
}, 100).then(function(results) {
// process results here
}, function(err) {
// error here
});
Your attempts at a structure like this:
return getNextItem() !== false;
Can't work because getNextItem() returns a promise which is always !== false since a promise is an object so that can't work. If you want to test a promise, you have to use .then() to get its value and you have to do the comparson asynchronously so you can't directly return a value like that.
Note: While these implementations use a function that calls itself, this does not cause stack build-up because they call themselves asynchronously. That means the stack has already completely unwound before the function calls itself again, thus there is no stack build-up. This will always be the case from a .then() handler since the Promise specification requires that a .then() handler is not called until the stack has returned to "platform code" which means it has unwound all regular "user code" before calling the .then() handler.
Using async and await in ES7
In ES7, you can use async and await to "pause" a loop. That can make this type of iteration a lot simpler to code. This looks structurally more like a typical synchronous loop. It uses await to wait on promises and because the function is declared async, it always returns a promise:
function delay(t) {
return new Promise(resolve => {
setTimeout(resolve, t);
});
}
async function processLoop(mainFn, compareFn, timeDelay) {
var results = [];
// loop until condition is met
while (true) {
let val = await mainFn();
results.push(val);
if (compareFn(val)) {
return results;
} else {
if (timeDelay) {
await delay(timeDelay);
}
}
}
}
processLoop(getNextItem, function(val) {
return val < 100;
}, 100).then(function(results) {
// process results here
}, function(err) {
// error here
});

How do you know when an indefinitely long promise chain has completely finished?

I was trying to use promises to force serialization of a series of Ajax calls. These Ajax calls are made one for each time a user presses a button. I can successfully serialize the operations like this:
// sample async function
// real-world this is an Ajax call
function delay(val) {
log("start: ", val);
return new Promise(function(resolve) {
setTimeout(function() {
log("end: ", val);
resolve();
}, 500);
});
}
// initialize p to a resolved promise
var p = Promise.resolve();
var v = 1;
// each click adds a new task to
// the serially executed queue
$("#run").click(function() {
// How to detect here that there are no other unresolved .then()
// handlers on the current value of p?
p = p.then(function() {
return delay(v++);
});
});
Working demo: http://jsfiddle.net/jfriend00/4hfyahs3/
But, this builds a potentially never ending promise chain since the variable p that stores the last promise is never cleared. Every new operation just chains onto the prior promise. So, I was thinking that for good memory management, I should be able to detect when there are no more .then() handlers left to run on the current value of p and I can then reset the value of p, making sure that any objects that the previous chain of promise handlers might have held in closures will be eligible for garbage collection.
So, I was wondering how I would know in a given .then() handler that there are no more .then() handlers to be called in this chain and thus, I can just do p = Promise.resolve() to reset p and release the previous promise chain rather than just continually adding onto it.
I'm being told that a "good" promise implementation would not cause accumulating memory from an indefinitely growing promise chain. But, there is apparently no standard that requires or describes this (other than good programming practices) and we have lots of newbie Promise implementations out there so I have not yet decided if it's wise to rely on this good behavior.
My years of coding experience suggest that when implementations are new, facts are lacking that all implementations behave a certain way and there's no specification that says they should behave that way, then it might be wise to write your code in as "safe" a way as possible. In fact, it's often less work to just code around an uncertain behavior than it is to go test all relevant implementations to find out how they behave.
In that vein, here's an implementation of my code that seems to be "safe" in this regard. It just saves a local copy of the global last promise variable for each .then() handler and when that .then() handler runs, if the global promise variable still has the same value, then my code has not chained any more items onto it so this must be the currently last .then() handler. It seems to work in this jsFiddle:
// sample async function
// real-world this is an Ajax call
function delay(val) {
log("start: ", val);
return new Promise(function(resolve) {
setTimeout(function() {
log("end: ", val);
resolve();
}, 500);
});
}
// initialize p to a resolved promise
var p = Promise.resolve();
var v = 1;
// each click adds a new task to
// the serially executed queue
$("#run").click(function() {
var origP = p = p.then(function() {
return delay(v++);
}).then(function() {
if (p === origP) {
// no more are chained by my code
log("no more chained - resetting promise head");
// set fresh promise head so no chance of GC leaks
// on prior promises
p = Promise.resolve();
v = 1;
}
// clear promise reference in case this closure is leaked
origP = null;
}, function() {
origP = null;
});
});
… so that I can then reset the value of p, making sure that any objects that the previous chain of promise handlers might have held in closures will be eligible for garbage collection.
No. A promise handler that has been executed (when the promise has settled) is no more needed and implicitly eligible for garbage collection. A resolved promise does not hold onto anything but the resolution value.
You don't need to do "good memory management" for promises (asynchronous values), your promise library does take care of that itself. It has to "release the previous promise chain" automatically, if it doesn't then that's a bug. Your pattern works totally fine as is.
How do you know when the promise chain has completely finished?
I would take a pure, recursive approach for this:
function extendedChain(p, stream, action) {
// chains a new action to p on every stream event
// until the chain ends before the next event comes
// resolves with the result of the chain and the advanced stream
return Promise.race([
p.then(res => ({res}) ), // wrap in object to distinguish from event
stream // a promise that resolves with a .next promise
]).then(({next, res}) =>
next
? extendedChain(p.then(action), next, action) // a stream event happened first
: {res, next:stream}; // the chain fulfilled first
);
}
function rec(stream, action, partDone) {
return stream.then(({next}) =>
extendedChain(action(), next, action).then(({res, next}) => {
partDone(res);
return rec(next, action, partDone);
});
);
}
var v = 1;
rec(getEvents($("#run"), "click"), () => delay(v++), res => {
console.log("all current done, none waiting");
console.log("last result", res);
}); // forever
with a helper function for event streams like
function getEvents(emitter, name) {
var next;
function get() {
return new Promise((res) => {
next = res;
});
}
emitter.on(name, function() {
next({next: get()});
});
return get();
}
(Demo at jsfiddle.net)
It is impossible to detect when no more handlers are added.
It is in fact an undecidable problem. It is not very hard to show a reduction to the halting (or the Atm problem). I can add a formal reduction if you'd like but in handwavey: Given an input program, put a promise at its first line and chain to it at every return or throw - assuming we have a program that solves the problem you describe in this question - apply it to the input problem - we now know if it runs forever or not solving the halting problem. That is, your problem is at least as hard as the halting problem.
You can detect when a promise is "resolved" and update it on new ones.
This is common in "last" or in "flatMap". A good use case is autocomplete search where you only want the latest results. Here is an [implementation by Domenic
(https://github.com/domenic/last):
function last(operation) {
var latestPromise = null; // keep track of the latest
return function () {
// call the operation
var promiseForResult = operation.apply(this, arguments);
// it is now the latest operation, so set it to that.
latestPromise = promiseForResult;
return promiseForResult.then(
function (value) {
// if we are _still_ the last value when it resovled
if (latestPromise === promiseForResult) {
return value; // the operation is done, you can set it to Promise.resolve here
} else {
return pending; // wait for more time
}
},
function (reason) {
if (latestPromise === promiseForResult) { // same as above
throw reason;
} else {
return pending;
}
}
);
};
};
I adapted Domenic's code and documented it for your problem.
You can safely not optimize this
Sane promise implementations do not keep promises which are "up the chain", so setting it to Promise.resolve() will not save memory. If a promise does not do this it is a memory leak and you should file a bug against it.
I tried to check if we can see the promise's state in code, apprantly that is only possible from console, not from code, so I used a flag to moniter the status, not sure if there is a loophole somewhere:
var p
, v = 1
, promiseFulfilled = true;
function addPromise() {
if(!p || promiseFulfilled){
console.log('reseting promise...');
p = Promise.resolve();
}
p = p.then(function() {
promiseFulfilled = false;
return delay(v++);
}).then(function(){
promiseFulfilled = true;
});
}
fiddle demo
You could push the promises onto an array and use Promise.all:
var p = Promise.resolve,
promiseArray = [],
allFinishedPromise;
function cleanup(promise, resolvedValue) {
// You have to do this funkiness to check if more promises
// were pushed since you registered the callback, though.
var wereMorePromisesPushed = allFinishedPromise !== promise;
if (!wereMorePromisesPushed) {
// do cleanup
promiseArray.splice(0, promiseArray.length);
p = Promise.resolve(); // reset promise
}
}
$("#run").click(function() {
p = p.then(function() {
return delay(v++);
});
promiseArray.push(p)
allFinishedPromise = Promise.all(promiseArray);
allFinishedPromise.then(cleanup.bind(null, allFinishedPromise));
});
Alternatively, since you know they are executed sequentially, you could have each completion callback remove that promise from the array and just reset the promise when the array is empty.
var p = Promise.resolve(),
promiseArray = [];
function onPromiseComplete() {
promiseArray.shift();
if (!promiseArray.length) {
p = Promise.resolve();
}
}
$("#run").click(function() {
p = p.then(function() {
onPromiseComplete();
return delay(v++);
});
promiseArray.push(p);
});
Edit: If the array is likely to get very long, though, you should go with the first option b/c shifting the array is O(N).
Edit: As you noted, there's no reason to keep the array around. A counter will work just fine.
var p = Promise.resolve(),
promiseCounter = 0;
function onPromiseComplete() {
promiseCounter--;
if (!promiseCounter) {
p = Promise.resolve();
}
}
$("#run").click(function() {
p = p.then(function() {
onPromiseComplete();
return delay(v++);
});
promiseCounter++;
});

Categories

Resources