Changing callback into a promise chain - javascript

I was trying to start using promise chaining (was using callbacks so far), and I wanted to edit this code:
Account.findById(req.user._id,
(err, acc) => {
if (err) console.log(err);
var r = req.body;
acc.fullName = r.fullName;
acc.displayname = r.username;
acc.city = r.city;
acc.province = r.province;
acc.postalCode = r.postalCode;
acc.phone = r.phone;
acc.ageGroup = r.ageGroup;
acc.education = r.education;
acc.lookingForWork = r.lookingForWork;
acc.employmentStatus = r.employmentStatus;
acc.workingWithEOESC = r.workingWithEOESC;
acc.resume = r.resume;
acc.mainWorkExp = r.mainWorkExp;
acc.save();
res.redirect('/seeker');
})
This is what I tried to do:
Account.findById(req.user._id)
.then((err, acc) => {
if (err) console.log(err);
var r = req.body;
acc.fullName = r.fullName;
acc.displayname = r.username;
acc.city = r.city;
acc.province = r.province;
acc.postalCode = r.postalCode;
acc.phone = r.phone;
acc.ageGroup = r.ageGroup;
acc.education = r.education;
acc.lookingForWork = r.lookingForWork;
acc.employmentStatus = r.employmentStatus;
acc.workingWithEOESC = r.workingWithEOESC;
acc.resume = r.resume;
acc.mainWorkExp = r.mainWorkExp;
acc.save();
})
.catch(e => console.log(e))
.then((acc) => {
console.log(acc);
res.redirect('/seeker');
})
});
But the promise version throws a TypeError: Cannot set property 'fullName' of undefined error.
The changes are not being saved and console loging the acc results in undefined. Forgot to add that in the post
I'm just learning promises. What am I missing? The inside code is almost exactly the same.

.then functions in promises can take maxiumum two argument which must be both functions, the first function is when the promise is fullfilled, while the second function is when the promise is rejected, alternatively you can pass in only one function to .then and use .catch to handle any kind of error or a rejected promise
var f1 = acc => console.log(acc); // logs out the acc object;
var f2 = err => console.log(err); // logs out error while executing the promise
.then(f1,f2); // when you do this there is no need for a catch block
// or
.then( acc => {
console.log(acc) // logs out the acc object
}).catch( err => console.log(err) ) //logs out the error
// if you need to handle another value
.then( acc => {
console.log(acc);
return acc.save(); //lets say acc.save() returns an object
}).then( acc => console.log(acc) ); // the value of acc.save() is passed down to the next `.then` block

Callback-based API's have a common convention of using the first argument of the callback function to indicate failure. Promises require no such convention, because they have built-in means of handling failures, so you need to just operate on the first argument, not the second. The second argument will be undefined, resulting in the error you're seeing.
Most of the time when you're translating callback-based code to promise-based code, you want to use this pattern as your basic guide:
// Callback-based:
asyncFn((err, result) => {
if (err) {
// handle failure
} else {
// handle success
}
});
// Promise-based equivalent:
asyncFnPromise()
.then((result) => {
// handle success
}, (err) => {
// handle failure
});
// Alternative promised-based:
asyncFnPromise()
.then((result) => {
// handle success.
// Note that unlike the above, any errors thrown here will trigger
// the `catch` handler below, in addition to actual asyncFnPromise
// failures.
})
.catch((err) => {
// handle failure
});

then is just called on success, therefore there is definetly no error:
then((acc) => {

It is happening because the function 'findById' probably not returning 'promise' and just returning some response.You need to create a 'promise object' in findById function and return it.
findById (){
let promise = new Promise((resolve, reject) => {
Suppose results is yield from some async task, so when
//wanted results occured
resolve(value);
//unwanted result occured
reject(new Error('Something happened!'));
return promise;
}
findById.then(response => {
console.log(response);
}, error => {
console.log(error);
});

Related

Why does a chained promise's .then() runs after the prior promise failed with .catch()? [duplicate]

This question already has answers here:
Chained promises not passing on rejection
(4 answers)
Closed 1 year ago.
I have three functions functionA, functionB, and functionC.
Why does the .then(id)... block inside functionC run even though functionB throws an error and ends with the catch statement.
I'm sure I'm missing something important about promises.
const functionA = () => {
return fetch("http://localhost:3001/types")
.then((response) => response.json())
.then((data) => data)
.catch((error) => error);
};
const functionB = (type) => {
return functionA()
.then((response) => response.filter((item) => item.name === type))
.then((result) => {
if (result.length !== 1) { // assume for this example this is truthy
const error = new Error("no type found or found more than one");
throw error;
} else {
return result[0].id;
}
})
.catch((error) => error); // this runs as expected since we're throwing an error above
};
const functionC = (x, y) => {
return functionB(y)
.then((id) => { //why does this block run?
//..do something
})
.catch((error) => console.log("functionB threw an error"));
};
Your catch handler converts rejection into fulfillment with the error value. Just remove the .catch((error) => error); entirely, so that the rejection propagates to the caller.
This (which is basically what you have, just spread across two functions):
doSomething()
.then(x => { /* ...do something with x...*/ })
.catch(error => error)
.then(y => { /* ...do something with y...*/ });
is roughly analogous to this synchronous code:
let y;
try {
const x = doSomething();
y = /* ...do something with x... */;
} catch (error) {
y = error;
}
/* ...do something with y... */;
Catching the error and then completing normally suppresses the error.
In general, non-async code using promises should do one of two things:
Handle the error locally — this is usually only top-level entry points like your main function (if you have one) or event handlers.
Return the result of calling then without using catch, so that rejections propagate to the outermost level (where, hopefully, they're handled because of #1 above).
FYI, that's basically what async/await does automatically for you (ignoring a lot of details). Just for what it's worth, here's that code (slightly modified) using async/await, assuming functionC is the top-level entry point where errors should be handled:
const functionA = async () => {
const response = await fetch("http://localhost:3001/types");
if (!response.ok) { // (You're missing this check)
throw new Error(`HTTP error ${response.status}`);
}
return response.json();
};
const functionB = async (type) => {
const result = (await functionA()).filter(item => item.name === type);
if (result.length !== 1) {
const error = new Error("no type found or found more than one");
throw error;
}
return result[0].id;
};
const functionC = async (x, y) => {
// I'm assuming this is your top-level entry point, so we'll handle
// errors here
try {
const id = await functionB(y);
//..do something
} catch (error) {
console.log("functionB threw an error");
}
};

Get value returned from Promise in JavaScript

I´m new to JavaScript and a little bit confused about Promises, this is what I have:
export const testFunction = () => dispatch => {
otherFunction().then(( response ) => {
//do something...
return response;
}).catch(( error ) => {
//do something...
return error;
});
}
In another file I'm trying to get the value returned from the then like this:
let result = this.props.testFunction()
And like this:
let result = this.props.testFunction ().then(( result ) => {
console.log(result);
}).catch(( error ) => {
console.log(result); // undefined
});
But I get undefined as the result, what is the correct way of getting that value?
testFunction is not returning a Promise so you can't use then or catch and returns undefined because well, it's not returning any thing.
Try to return a promise like the example below however I am not sure what the dispatch argument supposed to do so I have removed it and hopefully this'll help:
export const testFunction = () => {
return new Promise((resolve, reject) => {
otherFunction().then(( response ) => {
//do something...
resolve(response);
}).catch(( error ) => {
//do something...
reject(error);
});
});
}
when you are trying to return a promise to use it in another file, you must use the following syntax:
const testFunction = () => {
return new Promise((resolve, reject) => {
if (error) {
return reject(error); // this is the value sent to .catch(error)
}
return resolve(valueToReturn); // this is the value sent to .then(result)
});
}
This is how you create a promise to use where you want, if it has an error it will sent to catch block, otherwise you should see the console.log(result) value.
And in your external file you can use the syntax that you are using, try it in that way to see the console.log value.
Here is a link to see more information: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

How to wrap a Javascript function so all errors are caught including Promise rejections

I want to write a function used to wrap other functions so all errors are caught, including errors generated by a Promise rejection (which normally requires the .catch Promise method).
The goal is to be able to wrap functions so all runtime errors are handled. An example usage is a function we want to run, but that is optional and not part of the core business flow. If there is an error, we want to report it and later fix it, but we do not want it to stop program flow.
It should be able to wrap functions with any number of arguments, and return the same value as the original function, including if the original function returns a promise.
Am I handling all possible cases below? Is there a simpler way to do this?
const catchAllErrors = (fn) => (...args) => {
try {
const possiblePromise = fn(...args);
// Is it a promise type object? Can't use "instanceof Promise" because just
// for example, Bluebird promise library is not an instance of Promise.
if (typeof possiblePromise.catch === 'function') {
return Promise.resolve(possiblePromise).catch((error) => {
console.log('Caught promise error.', error);
});
}
return possiblePromise;
} catch (error) {
console.log('Caught error.', error);
}
};
// EXAMPLE USAGE
// Applying the wrapper to various types of functions:
const throwsErr = catchAllErrors((x, y) => {
throw `Error 1 with args ${x}, ${y}.`;
});
const promiseErr = catchAllErrors((a, b) => Promise.reject(`Error 2 with args ${a}, ${b}.`));
const noError = catchAllErrors((name) => `Hi there ${name}.`);
const noErrorPromise = catchAllErrors((wish) => Promise.resolve(`I wish for ${wish}.`));
// Running the wrapped functions:
console.log(throwsErr(1, 2));
promiseErr(3, 4).then((result) => console.log(result));
console.log(noError('folks'));
noErrorPromise('sun').then((result) => console.log(result));
Don't try to detect whether something is a promise or not yourself. Use the builtin thenable detection of promise resolution. You can use either the Promise constructor to catch the exception:
const catchAllErrors = (fn) => (...args) => {
return new Promise(resolve => {
resolve(fn(...args));
}).catch((error) => {
console.log('Caught error.', error);
});
};
or just go for async/await syntax:
const catchAllErrors = (fn) => async (...args) => {
try {
return await fn(...args);
} catch (error) {
console.log('Caught error.', error);
}
};
(If you used Bluebird anyway, you could also call its Promise.try method for this purpose)

Javascript Promise.all() method to fire after all errors and success – surprised that finally() doesnt do this [duplicate]

Let's say I have a set of Promises that are making network requests, of which one will fail:
// http://does-not-exist will throw a TypeError
var arr = [ fetch('index.html'), fetch('http://does-not-exist') ]
Promise.all(arr)
.then(res => console.log('success', res))
.catch(err => console.log('error', err)) // This is executed
Let's say I want to wait until all of these have finished, regardless of if one has failed. There might be a network error for a resource that I can live without, but which if I can get, I want before I proceed. I want to handle network failures gracefully.
Since Promise.all doesn't leave any room for this, what is the recommended pattern for handling this, without using a promises library?
Update, you probably want to use the built-in native Promise.allSettled:
Promise.allSettled([promise]).then(([result]) => {
//reach here regardless
// {status: "fulfilled", value: 33}
});
As a fun fact, this answer below was prior art in adding that method to the language :]
Sure, you just need a reflect:
const reflect = p => p.then(v => ({v, status: "fulfilled" }),
e => ({e, status: "rejected" }));
reflect(promise).then((v) => {
console.log(v.status);
});
Or with ES5:
function reflect(promise){
return promise.then(function(v){ return {v:v, status: "fulfilled" }},
function(e){ return {e:e, status: "rejected" }});
}
reflect(promise).then(function(v){
console.log(v.status);
});
Or in your example:
var arr = [ fetch('index.html'), fetch('http://does-not-exist') ]
Promise.all(arr.map(reflect)).then(function(results){
var success = results.filter(x => x.status === "fulfilled");
});
Similar answer, but more idiomatic for ES6 perhaps:
const a = Promise.resolve(1);
const b = Promise.reject(new Error(2));
const c = Promise.resolve(3);
Promise.all([a, b, c].map(p => p.catch(e => e)))
.then(results => console.log(results)) // 1,Error: 2,3
.catch(e => console.log(e));
const console = { log: msg => div.innerHTML += msg + "<br>"};
<div id="div"></div>
Depending on the type(s) of values returned, errors can often be distinguished easily enough (e.g. use undefined for "don't care", typeof for plain non-object values, result.message, result.toString().startsWith("Error:") etc.)
Benjamin's answer offers a great abstraction for solving this issue, but I was hoping for a less abstracted solution. The explicit way to to resolve this issue is to simply call .catch on the internal promises, and return the error from their callback.
let a = new Promise((res, rej) => res('Resolved!')),
b = new Promise((res, rej) => rej('Rejected!')),
c = a.catch(e => { console.log('"a" failed.'); return e; }),
d = b.catch(e => { console.log('"b" failed.'); return e; });
Promise.all([c, d])
.then(result => console.log('Then', result)) // Then ["Resolved!", "Rejected!"]
.catch(err => console.log('Catch', err));
Promise.all([a.catch(e => e), b.catch(e => e)])
.then(result => console.log('Then', result)) // Then ["Resolved!", "Rejected!"]
.catch(err => console.log('Catch', err));
Taking this one step further, you could write a generic catch handler that looks like this:
const catchHandler = error => ({ payload: error, resolved: false });
then you can do
> Promise.all([a, b].map(promise => promise.catch(catchHandler))
.then(results => console.log(results))
.catch(() => console.log('Promise.all failed'))
< [ 'Resolved!', { payload: Promise, resolved: false } ]
The problem with this is that the caught values will have a different interface than the non-caught values, so to clean this up you might do something like:
const successHandler = result => ({ payload: result, resolved: true });
So now you can do this:
> Promise.all([a, b].map(result => result.then(successHandler).catch(catchHandler))
.then(results => console.log(results.filter(result => result.resolved))
.catch(() => console.log('Promise.all failed'))
< [ 'Resolved!' ]
Then to keep it DRY, you get to Benjamin's answer:
const reflect = promise => promise
.then(successHandler)
.catch(catchHander)
where it now looks like
> Promise.all([a, b].map(result => result.then(successHandler).catch(catchHandler))
.then(results => console.log(results.filter(result => result.resolved))
.catch(() => console.log('Promise.all failed'))
< [ 'Resolved!' ]
The benefits of the second solution are that its abstracted and DRY. The downside is you have more code, and you have to remember to reflect all your promises to make things consistent.
I would characterize my solution as explicit and KISS, but indeed less robust. The interface doesn't guarantee that you know exactly whether the promise succeeded or failed.
For example you might have this:
const a = Promise.resolve(new Error('Not beaking, just bad'));
const b = Promise.reject(new Error('This actually didnt work'));
This won't get caught by a.catch, so
> Promise.all([a, b].map(promise => promise.catch(e => e))
.then(results => console.log(results))
< [ Error, Error ]
There's no way to tell which one was fatal and which was wasn't. If that's important then you're going to want to enforce and interface that tracks whether it was successful or not (which reflect does).
If you just want to handle errors gracefully, then you can just treat errors as undefined values:
> Promise.all([a.catch(() => undefined), b.catch(() => undefined)])
.then((results) => console.log('Known values: ', results.filter(x => typeof x !== 'undefined')))
< [ 'Resolved!' ]
In my case, I don't need to know the error or how it failed--I just care whether I have the value or not. I'll let the function that generates the promise worry about logging the specific error.
const apiMethod = () => fetch()
.catch(error => {
console.log(error.message);
throw error;
});
That way, the rest of the application can ignore its error if it wants, and treat it as an undefined value if it wants.
I want my high level functions to fail safely and not worry about the details on why its dependencies failed, and I also prefer KISS to DRY when I have to make that tradeoff--which is ultimately why I opted to not use reflect.
There is a finished proposal for a function which can accomplish this natively, in vanilla Javascript: Promise.allSettled, which has made it to stage 4, is officialized in ES2020, and is implemented in all modern environments. It is very similar to the reflect function in this other answer. Here's an example, from the proposal page. Before, you would have had to do:
function reflect(promise) {
return promise.then(
(v) => {
return { status: 'fulfilled', value: v };
},
(error) => {
return { status: 'rejected', reason: error };
}
);
}
const promises = [ fetch('index.html'), fetch('https://does-not-exist/') ];
const results = await Promise.all(promises.map(reflect));
const successfulPromises = results.filter(p => p.status === 'fulfilled');
Using Promise.allSettled instead, the above will be equivalent to:
const promises = [ fetch('index.html'), fetch('https://does-not-exist/') ];
const results = await Promise.allSettled(promises);
const successfulPromises = results.filter(p => p.status === 'fulfilled');
Those using modern environments will be able to use this method without any libraries. In those, the following snippet should run without problems:
Promise.allSettled([
Promise.resolve('a'),
Promise.reject('b')
])
.then(console.log);
Output:
[
{
"status": "fulfilled",
"value": "a"
},
{
"status": "rejected",
"reason": "b"
}
]
For older browsers, there is a spec-compliant polyfill here.
I really like Benjamin's answer, and how he basically turns all promises into always-resolving-but-sometimes-with-error-as-a-result ones. :)
Here's my attempt at your request just in case you were looking for alternatives. This method simply treats errors as valid results, and is coded similar to Promise.all otherwise:
Promise.settle = function(promises) {
var results = [];
var done = promises.length;
return new Promise(function(resolve) {
function tryResolve(i, v) {
results[i] = v;
done = done - 1;
if (done == 0)
resolve(results);
}
for (var i=0; i<promises.length; i++)
promises[i].then(tryResolve.bind(null, i), tryResolve.bind(null, i));
if (done == 0)
resolve(results);
});
}
var err;
Promise.all([
promiseOne().catch(function(error) { err = error;}),
promiseTwo().catch(function(error) { err = error;})
]).then(function() {
if (err) {
throw err;
}
});
The Promise.all will swallow any rejected promise and store the error in a variable, so it will return when all of the promises have resolved. Then you can re-throw the error out, or do whatever. In this way, I guess you would get out the last rejection instead of the first one.
I had the same problem and have solved it in the following way:
const fetch = (url) => {
return node-fetch(url)
.then(result => result.json())
.catch((e) => {
return new Promise((resolve) => setTimeout(() => resolve(fetch(url)), timeout));
});
};
tasks = [fetch(url1), fetch(url2) ....];
Promise.all(tasks).then(......)
In that case Promise.all will wait for every Promise will come into resolved or rejected state.
And having this solution we are "stopping catch execution" in a non-blocking way. In fact, we're not stopping anything, we just returning back the Promise in a pending state which returns another Promise when it's resolved after the timeout.
This should be consistent with how Q does it:
if(!Promise.allSettled) {
Promise.allSettled = function (promises) {
return Promise.all(promises.map(p => Promise.resolve(p).then(v => ({
state: 'fulfilled',
value: v,
}), r => ({
state: 'rejected',
reason: r,
}))));
};
}
Instead of rejecting, resolve it with a object.
You could do something like this when you are implementing promise
const promise = arg => {
return new Promise((resolve, reject) => {
setTimeout(() => {
try{
if(arg != 2)
return resolve({success: true, data: arg});
else
throw new Error(arg)
}catch(e){
return resolve({success: false, error: e, data: arg})
}
}, 1000);
})
}
Promise.all([1,2,3,4,5].map(e => promise(e))).then(d => console.log(d))
Benjamin Gruenbaum answer is of course great,. But I can also see were Nathan Hagen point of view with the level of abstraction seem vague. Having short object properties like e & v don't help either, but of course that could be changed.
In Javascript there is standard Error object, called Error,. Ideally you always throw an instance / descendant of this. The advantage is that you can do instanceof Error, and you know something is an error.
So using this idea, here is my take on the problem.
Basically catch the error, if the error is not of type Error, wrap the error inside an Error object. The resulting array will have either resolved values, or Error objects you can check on.
The instanceof inside the catch, is in case you use some external library that maybe did reject("error"), instead of reject(new Error("error")).
Of course you could have promises were you resolve an error, but in that case it would most likely make sense to treat as an error anyway, like the last example shows.
Another advantage of doing it this, array destructing is kept simple.
const [value1, value2] = PromiseAllCatch(promises);
if (!(value1 instanceof Error)) console.log(value1);
Instead of
const [{v: value1, e: error1}, {v: value2, e: error2}] = Promise.all(reflect..
if (!error1) { console.log(value1); }
You could argue that the !error1 check is simpler than an instanceof, but your also having to destruct both v & e.
function PromiseAllCatch(promises) {
return Promise.all(promises.map(async m => {
try {
return await m;
} catch(e) {
if (e instanceof Error) return e;
return new Error(e);
}
}));
}
async function test() {
const ret = await PromiseAllCatch([
(async () => "this is fine")(),
(async () => {throw new Error("oops")})(),
(async () => "this is ok")(),
(async () => {throw "Still an error";})(),
(async () => new Error("resolved Error"))(),
]);
console.log(ret);
console.log(ret.map(r =>
r instanceof Error ? "error" : "ok"
).join(" : "));
}
test();
I think the following offers a slightly different approach... compare fn_fast_fail() with fn_slow_fail()... though the latter doesn't fail as such... you can check if one or both of a and b is an instance of Error and throw that Error if you want it to reach the catch block (e.g. if (b instanceof Error) { throw b; }) . See the jsfiddle.
var p1 = new Promise((resolve, reject) => {
setTimeout(() => resolve('p1_delayed_resolvement'), 2000);
});
var p2 = new Promise((resolve, reject) => {
reject(new Error('p2_immediate_rejection'));
});
var fn_fast_fail = async function () {
try {
var [a, b] = await Promise.all([p1, p2]);
console.log(a); // "p1_delayed_resolvement"
console.log(b); // "Error: p2_immediate_rejection"
} catch (err) {
console.log('ERROR:', err);
}
}
var fn_slow_fail = async function () {
try {
var [a, b] = await Promise.all([
p1.catch(error => { return error }),
p2.catch(error => { return error })
]);
console.log(a); // "p1_delayed_resolvement"
console.log(b); // "Error: p2_immediate_rejection"
} catch (err) {
// we don't reach here unless you throw the error from the `try` block
console.log('ERROR:', err);
}
}
fn_fast_fail(); // fails immediately
fn_slow_fail(); // waits for delayed promise to resolve
I just wanted a polyfill that exactly replicated ES2020 behaviour since I'm locked into node versions a lot earlier than 12.9 (when Promise.allSettled appeared), unfortunately. So for what it's worth, this is my version:
const settle = (promise) => (promise instanceof Promise) ?
promise.then(val => ({ value: val, status: "fulfilled" }),
err => ({ reason: err, status: "rejected" })) :
{ value: promise, status: 'fulfilled' };
const allSettled = async (parr) => Promise.all(parr.map(settle));
This handles a mixed array of promise and non-promise values, as does the ES version. It hands back the same array of { status, value/reason } objects as the native version.
Here's my custom settledPromiseAll()
const settledPromiseAll = function(promisesArray) {
var savedError;
const saveFirstError = function(error) {
if (!savedError) savedError = error;
};
const handleErrors = function(value) {
return Promise.resolve(value).catch(saveFirstError);
};
const allSettled = Promise.all(promisesArray.map(handleErrors));
return allSettled.then(function(resolvedPromises) {
if (savedError) throw savedError;
return resolvedPromises;
});
};
Compared to Promise.all
If all promises are resolved, it performs exactly as the standard one.
If one of more promises are rejected, it returns the first one rejected much the same as the standard one but unlike it waits for all promises to resolve/reject.
For the brave we could change Promise.all():
(function() {
var stdAll = Promise.all;
Promise.all = function(values, wait) {
if(!wait)
return stdAll.call(Promise, values);
return settledPromiseAll(values);
}
})();
CAREFUL. In general we never change built-ins, as it might break other unrelated JS libraries or clash with future changes to JS standards.
My settledPromiseall is backward compatible with Promise.all and extends its functionality.
People who are developing standards -- why not include this to a new Promise standard?
I recently built a library that allows what you need. it executes promises in parallel, and if one fails, the process continues, at the end it returns an array with all the results, including errors.
https://www.npmjs.com/package/promise-ax
I hope and it is helpful for someone.
const { createPromise } = require('promise-ax');
const promiseAx = createPromise();
const promise1 = Promise.resolve(4);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, new Error("error")));
const promise3 = Promise.reject("error");
const promise4 = promiseAx.resolve(8);
const promise5 = promiseAx.reject("errorAx");
const asyncOperation = (time) => {
return new Promise((resolve, reject) => {
if (time < 0) {
reject("reject");
}
setTimeout(() => {
resolve(time);
}, time);
});
};
const promisesToMake = [promise1, promise2, promise3, promise4, promise5, asyncOperation(100)];
promiseAx.allSettled(promisesToMake).then((results) => results.forEach((result) => console.log(result)));
// Salida esperada:
// 4
// Error: error
// error
// 8
// errorAx
// 100
I would do:
var err = [fetch('index.html').then((success) => { return Promise.resolve(success); }).catch((e) => { return Promise.resolve(e); }),
fetch('http://does-not-exist').then((success) => { return Promise.resolve(success); }).catch((e) => { return Promise.resolve(e); })];
Promise.all(err)
.then(function (res) { console.log('success', res) })
.catch(function (err) { console.log('error', err) }) //never executed
I've been using following codes since ES5.
Promise.wait = function(promiseQueue){
if( !Array.isArray(promiseQueue) ){
return Promise.reject('Given parameter is not an array!');
}
if( promiseQueue.length === 0 ){
return Promise.resolve([]);
}
return new Promise((resolve, reject) =>{
let _pQueue=[], _rQueue=[], _readyCount=false;
promiseQueue.forEach((_promise, idx) =>{
// Create a status info object
_rQueue.push({rejected:false, seq:idx, result:null});
_pQueue.push(Promise.resolve(_promise));
});
_pQueue.forEach((_promise, idx)=>{
let item = _rQueue[idx];
_promise.then(
(result)=>{
item.resolved = true;
item.result = result;
},
(error)=>{
item.resolved = false;
item.result = error;
}
).then(()=>{
_readyCount++;
if ( _rQueue.length === _readyCount ) {
let result = true;
_rQueue.forEach((item)=>{result=result&&item.resolved;});
(result?resolve:reject)(_rQueue);
}
});
});
});
};
The usage signature is just like Promise.all. The major difference is that Promise.wait will wait for all the promises to finish their jobs.
I know that this question has a lot of answers, and I'm sure must (if not all) are correct.
However it was very hard for me to understand the logic/flow of these answers.
So I looked at the Original Implementation on Promise.all(), and I tried to imitate that logic - with the exception of not stopping the execution if one Promise failed.
public promiseExecuteAll(promisesList: Promise<any>[] = []): Promise<{ data: any, isSuccess: boolean }[]>
{
let promise: Promise<{ data: any, isSuccess: boolean }[]>;
if (promisesList.length)
{
const result: { data: any, isSuccess: boolean }[] = [];
let count: number = 0;
promise = new Promise<{ data: any, isSuccess: boolean }[]>((resolve, reject) =>
{
promisesList.forEach((currentPromise: Promise<any>, index: number) =>
{
currentPromise.then(
(data) => // Success
{
result[index] = { data, isSuccess: true };
if (promisesList.length <= ++count) { resolve(result); }
},
(data) => // Error
{
result[index] = { data, isSuccess: false };
if (promisesList.length <= ++count) { resolve(result); }
});
});
});
}
else
{
promise = Promise.resolve([]);
}
return promise;
}
Explanation:
- Loop over the input promisesList and execute each Promise.
- No matter if the Promise resolved or rejected: save the Promise's result in a result array according to the index. Save also the resolve/reject status (isSuccess).
- Once all Promises completed, return one Promise with the result of all others.
Example of use:
const p1 = Promise.resolve("OK");
const p2 = Promise.reject(new Error(":-("));
const p3 = Promise.resolve(1000);
promiseExecuteAll([p1, p2, p3]).then((data) => {
data.forEach(value => console.log(`${ value.isSuccess ? 'Resolve' : 'Reject' } >> ${ value.data }`));
});
/* Output:
Resolve >> OK
Reject >> :-(
Resolve >> 1000
*/
You can execute your logic sequentially via synchronous executor nsynjs. It will pause on each promise, wait for resolution/rejection, and either assign resolve's result to data property, or throw an exception (for handling that you will need try/catch block). Here is an example:
function synchronousCode() {
function myFetch(url) {
try {
return window.fetch(url).data;
}
catch (e) {
return {status: 'failed:'+e};
};
};
var arr=[
myFetch("https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"),
myFetch("https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/NONEXISTANT.js"),
myFetch("https://ajax.NONEXISTANT123.com/ajax/libs/jquery/2.0.0/NONEXISTANT.js")
];
console.log('array is ready:',arr[0].status,arr[1].status,arr[2].status);
};
nsynjs.run(synchronousCode,{},function(){
console.log('done');
});
<script src="https://rawgit.com/amaksr/nsynjs/master/nsynjs.js"></script>
Promise.all with using modern async/await approach
const promise1 = //...
const promise2 = //...
const data = await Promise.all([promise1, promise2])
const dataFromPromise1 = data[0]
const dataFromPromise2 = data[1]
I don't know which promise library you are using, but most have something like allSettled.
Edit: Ok since you want to use plain ES6 without external libraries, there is no such method.
In other words: You have to loop over your promises manually and resolve a new combined promise as soon as all promises are settled.

angularjs chaining promises not working , and angular.copy is not either

So my understanding is that i can loop over my FIRST returned set of objects
Object = vm.liberty
Loop
vm.liberty.forEach(function (obj) {
My understanding of this obj is that it is a reference and thus the vm.liberty object will get updated.... right?
I know that console.log is not reliable as it spits out results as soon as line it hit
However
console.log('obj', obj);
That contains the DATA that i want, I have added in a extended nested object to each record in the object ....
But someone either i'm not doing promise chaining correctly or something else
I even try to do a angular.copy
vm.liberty2 = angular.copy(vm.liberty);
That does not work , as console.log(vm.liberty); seems to be empty
Here is my function:
var loadLiberty = function () {
var promise1 = libertyService.getLibertyQuestions();
promise1.then(function (response) {
vm.liberty = response;
}).then(function (res) {
vm.liberty.forEach(function (obj) {
promise2 = libertyService.getDirectives(obj.QuestionId)
.then(function (result) {
obj.directives = result;
console.log('obj', obj);
//vm.liberty.directives = result;
}, function (err) {
console.log('err', err);
});
});
});
//vm.liberty2 = angular.copy(vm.liberty);
console.log(vm.liberty);
vm.liberty2 = angular.copy(vm.liberty);
//return promise1;
};
Thoughts or suggestions ?
Your console.log(vm.liberty); happens before your promises are resolved, that is why it still has its initial value.
Have a look at the following example:
function loadLiberty() {
const libertyQuestionsPromise = libertyService.getLibertyQuestions();
libertyQuestionsPromise
.then(x => vm.liberty = x)
.then(() => {
// hold promises which are getting directives in this array
const promises = [];
vm.liberty.forEach(obj => {
// get directives for every obj
const promise = libertyService
.getDirectives(obj.QuestionId)
.then(x => obj.directives = x);
promises.push(promise);
});
// wait until all promises in the promises array are resolved
return $q.all(promises);
})
.then(() => {
// at this point the vm.liberty should be populated with results
console.log(vm.liberty);
})
.catch(error => {
// handle any error which might have occurred in any of the promises in this chain
});
}
Do not forget to inject the $q dependency in your service/controller.

Categories