Embed edit code is not working, how can i solve? - javascript

I have this code, but it doesn't work, it's returning some function, how can I solve it?
module.exports = (client, message, edit) => {
const embed = new Discord.RichEmbed
const { prefix } = require("./config.json")
if (message.content.toLowerCase().startsWith(prefix + 'edit')) {
let messageID = message.content.split(' ').slice(1, 2).join(' ');
let myMessage = message.content.split(' ').slice(2).join(' ');
if (!messageID) return;
if (!myMessage) return;
message.channel.fetchMessage(messageID)
.then(m => m.edit(embed)
.then(message.delete().catch(() => { return })))
.catch(e => console.error(e));
}
}

.then(message.delete().catch(() => { return })))
That line is the source of your problem. Promise.then() takes a callback function, but you haven't provided it with one. What's actually being passed to it is the value returned by message.delete().catch(() => { return })), which is a pending promise.
This is the (working) equivalent to what it looks like you're attempting to accomplish:
message.channel.fetchMessage(messageID)
.then(m => m.edit(embed)) // will run after above (unless above was rejected)
.then(m => m.delete()) // will run after above (unless above was rejected)
.catch(console.error); // will catch errors from any of the 3 promises

Related

Function gets trigger before promise is resolved

I have this two functions, writedb is supposed to act only when uploadImages is done. I'm not getting what I'm hoping for.
The resolve shows "null" for "this.imageURL" as is predefined but is triggered before the console.log that are inside the loop which show the correct wanted information.
uploadImages() {
return new Promise(async (resolve, reject) => {
let newImageURL = []
for (const path of this.images){
fs.readFile(path,
async (err, data) => {
const ref = st.ref(`images/${this.id}/${path.split('/').pop()}`);
await ref.put(data);
let url = await ref.getDownloadURL();
newImageURL.push(url);
this.imageURL = newImageURL;
console.log(this.imageURL);
})
}
resolve(console.log(this.name),
console.log(this.imageURL),
console.log('Done upload images'))
})
}
writedb(){
(() => {
let obj = {};
this.uploadImages().then(
console.log('writedb in action...'),
db.collection("products").doc(this.id).set(Object.assign(obj, this))
)
})();
}
What am I doing wrong here? How can i get the promise to resolve only if the for loop is done??
Use fs.promises and Promise.all to wait for each readFile to resolve - and avoid the explicit Promise construction antipattern:
uploadImages() {
return Promise.all(this.images.map(
path => fs.promises.readFile(async (data) => {
const ref = st.ref(`images/${this.id}/${path.split('/').pop()}`);
await ref.put(data);
return ref.getDownloadURL();
})
));
}
Then instance.uploadImages() will return a Promise that resolves to an array of the data you want. uploadImages().then((result) => { /* do stuff with result */ }).catch(handleErrors);
With CertainPerformance help I've corrected my code as follows
Thank you! After some though I finally was able to implement it and achieve the expected result.
uploadImages (){
return Promise.all(this.imagespath.map(
path => fs.promises.readFile(path)
.then(async (data)=>{
const ref = st.ref(`images/${this.id}/${path.split('/').pop()}`);
await ref.put(data);
return ref.getDownloadURL();
})
))
}
writedb(){
this.uploadImages()
.then((res)=>{
this.imageURL = res;
//Firebase no toma custom objects Object.assign(obj, this)
let obj ={}
Object.assign(obj, this);
delete obj.imagespath;
db.collection("products").doc(this.id).set(obj)
}).catch( err=>console.log(err) )
}

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.

How to do recursive requests to an API with promise in Node.js?

I need to read data from API that only gives 100 results per query and a timestamp from where to get the next 100.
I've managed to do multiple requests one after another with the code below, but for some reason it never returns to the original promise. It gets stuck on the "No more orders to fetch".
app.get('/test', (req, res) => {
const getOrders = (from) => {
return request(mcfApiUrl + "changes?created_after_ts="+from+"&key="+mcfKey)
.then(xml => convert.xmlDataToJSON(xml,{explicitArray:false,mergeAttrs:true}))
.then(orders => checkForMore(orders));
}
const checkForMore = (orders) => {
return new Promise((resolve, reject) => {
if (orders['Orders']['orders'] == 100){
getOrders(orders['Orders']['time_to']);
console.log("Fetched "+ orders['Orders']['orders']+" orders");
console.log("More orders available from: "+moment(orders['Orders']['time_to']*1000).format());
}
else {
console.log("Fetched "+ orders['Orders']['orders']+" orders");
console.log("No more orders to fetch");
resolve(orders);
}
});
};
var fromdate = 1483999200;
getOrders(fromdate)
.then(output => res.send("Done")) // It never gets here
.catch(err => console.log(err));
});
What am I missing?
Your issue is that you're not resolving the checkForMore promise for all options.
const checkForMore = (orders) => {
return new Promise((resolve, reject) => {
if (orders['Orders']['orders'] == 100){
getOrders(orders['Orders']['time_to']); // <-- not resolved
}
else {
resolve(orders);
}
});
};
Just wrapping the call to getOrders with resolve will fix that.
resolve(getOrders(orders['Orders']['time_to']))
However, you don't really need to create a new promise:
const checkForMore = (orders) =>
orders['Orders']['orders'] == 100
? getOrders(orders['Orders']['time_to'])
: Promise.resolve(orders);
In fact, your entire function can be shrunk into a few lines:
const getOrders = (from) =>
request(mcfApiUrl + "changes?created_after_ts="+from+"&key="+mcfKey)
.then(xml => convert.xmlDataToJSON(xml,{explicitArray:false,mergeAttrs:true}))
.then(orders =>
orders.Orders.orders == 100
? getOrders(orders.Orders.time_to)
: Promise.resolve(orders)
);
Now, if you want to accumulate all the orders, you need to maintain some state through the recursion levels.
You can do that either with a global state or an additional parameter:
const getOrders = (from, allOrders = []) =>
// ^ accumulation container
request(mcfApiUrl + "changes?created_after_ts="+from+"&key="+mcfKey)
.then(xml => convert.xmlDataToJSON(xml,{explicitArray:false,mergeAttrs:true}))
.then(orders => {
allOrders.push(orders); // <-- accumulate
return orders.Orders.orders == 100
? getOrders(orders.Orders.time_to, allOrders) // <-- pass through recursion
: Promise.resolve(allOrders)
});

Promise then and catch clauses not working

Background
I have a multitude of functions that may or may not return a Promise and that may or may not fail, like in the examples below:
let goodFun = (fruit) => `I like ${fruit}`;
let badFun = (fruit) => {
throw `${fruit} is spoiled!`;
};
let badPromise = (fruit) => new Promise( (fulfil, reject) => {
reject(`Promise failed with ${fruit}`);
});
let goodPromise = (fruit) => new Promise((fulfil, reject) => {
fulfil(`Promise succeeded with ${fruit}`);
});
Because each function may or may not return a promise, which may or may not fail, I modified executeSafe from another StackOverflow post, that takes a function and its arguments as parameters, and then Promisifies the function and its result:
let executeSafe =
(fun, ...args) => Promise.resolve().then(() => {
return fun(...args);
});
My objective with all of this, is to have an asyncFun function that waits for the execution of a batch of functions that were Promisified and then returns whatever came from executing them:
let asyncFun =
(fruit) =>
Promise.all([badFun, goodFun, badPromise, goodPromise].map(
fun => executeSafe(fun, fruit)
)
);
Problem
asyncFun is designed to run the multitude of functions previously described and some of them I actually expect to see fail. To accommodate for this my asyncFun function has a catch clause. This clause only works with badFun and doesn't work with badPromise. The then clause never works.
let executor = () => {
let fruitsArr = ["banana", "orange", "apple"];
let results = [];
for (let fruit of fruitsArr)
results.push(
asyncFun(fruit)
.then(res => {
console.log(res);
})
.catch(error => {
console.log(`Error: ${error}`);
})
);
return Promise.all(results);
};
Not even placing the catch clause in the executor's call catches anything else other than the badFun errors.
executor()
.catch(error => console.log("Failed miserably to catch error!"));
Code
let goodFun = (fruit) => {
return `I like ${fruit}`;
};
let badFun = (fruit) => {
throw `${fruit} is spoiled!`;
};
let badPromise = (fruit) => Promise.resolve().then(() => {
throw `Promise failed with ${fruit}`;
});
let goodPromise = (fruit) => Promise.resolve().then(() => {
return `Promise succeded with ${fruit}`;
});
let executeSafe =
(fun, ...args) => Promise.resolve().then(() => {
return fun(...args);
});
let asyncFun = (fruit) => Promise.all([badFun, goodFun, badPromise, goodPromise].map(fun => executeSafe(fun, fruit)));
let executor = () => {
let fruitsArr = ["banana", "orange", "apple"];
let results = [];
for (let fruit of fruitsArr)
results.push(
asyncFun(fruit)
.then(res => {
console.log(res);
})
.catch(error => {
console.log(`Error: ${error}`);
})
);
return Promise.all(results);
};
executor()
.catch(error => console.log("Failed miserably to catch error!"));
Question:
How to fix my code so that its then and catch clauses in asyncFun work as intended?
The problem here is that you are misunderstanding how Promise.all() works. The way it works is that if all of the promises succeed, it resolves to an array of values. If any of them fail, it rejects with the first error that occurred.
What you seem to be expecting, that the successful ones are caught in the then and the failed ones are caught in the catch, is not possible. A promise either resolves once or it rejects once. It doesn't do both, and it won't do one or the other multiple times. Promise.all() returns a single promise so it will either resolve or reject.
Third party promise libraries do have methods for "settling" an array of promises - basically waiting until they have all done their thing (succeeded or failed), and resolving to an array of the results. You can implement it like this:
// values is an array of promises and/or non-promise values
function allSettled(values) {
let settle =
value => Promise.resolve(value)
.then(result => ({ state: "fulfilled", value: result }))
.catch(error => ({ state: "rejected", reason: error }));
return Promise.all(values.map(settle));
}
// example usage
allSettled(['hello', 'goodbye', Promise.resolve('good'), Promise.reject('bad')])
.then(results => console.log(results));
Then you can use it like below.
On an unrelated note, I've also modified your approach so that you don't need the modified version of executeSave that takes ...args (I think that's a convoluted way to go about it). You can create functions that use the arguments before you pass them into _executeSafe_:
let goodFun = (fruit) => `I like ${fruit}`;
let badFun = (fruit) => {
throw `${fruit} is spoiled!`;
};
let badPromise = (fruit) => Promise.reject(`Promise failed with ${fruit}`);
let goodPromise = (fruit) => Promise.resolve(`Promise succeeded with ${fruit}`);
let executeSafe = fun => Promise.resolve().then(fun);
function allSettled(values) {
let settle =
value => Promise.resolve(value)
.then(result => ({ state: "fulfilled", value: result }))
.catch(error => ({ state: "rejected", reason: error }));
return Promise.all(values.map(settle));
}
let asyncFun =
(fruit) =>
allSettled([badFun, goodFun, badPromise, goodPromise]
.map(fun => () => fun(fruit))
.map(executeSafe)
);
asyncFun("orange").then(results => console.log(results));
Additional side note - if you wanted to use the promisify function from Jared Smith's answer, then you could change your asyncFun function to this:
let asyncFun =
(fruit) =>
allSettled([badFun, goodFun, badPromise, goodPromise]
.map(promisify)
.map(fun => fun(fruit))
);
So this works, catches fine. If the function passed to promisify returns a promise, it just gets passed up the chain.
var fnThatThrows = s => { throw new Error(s) };
var identity = x => x;
var promisify = f => (...args) => new Promise((res, rej) => {
try {
res(f(...args));
} catch (e) {
rej(e);
}
});
var rejected = promisify(fnThatThrows);
rejected("foo").then(_ => console.log("shouldn't see")).catch(e => console.log(e)); // logs error
var succeded = promisify(identity);
succeeded("foo").then(val => console.log(val)); // logs 'foo'
If the promise from the argument function is rejected, that gets passed along too:
var returnsBadPromise = s => Promise.reject(new Error(s));
promisify(returnsBadPromise)("foo").catch(e => console.log(e)); // logs error

Is it safe to resolve a promise multiple times?

I have an i18n service in my application which contains the following code:
var i18nService = function() {
this.ensureLocaleIsLoaded = function() {
if( !this.existingPromise ) {
this.existingPromise = $q.defer();
var deferred = this.existingPromise;
var userLanguage = $( "body" ).data( "language" );
this.userLanguage = userLanguage;
console.log( "Loading locale '" + userLanguage + "' from server..." );
$http( { method:"get", url:"/i18n/" + userLanguage, cache:true } ).success( function( translations ) {
$rootScope.i18n = translations;
deferred.resolve( $rootScope.i18n );
} );
}
if( $rootScope.i18n ) {
this.existingPromise.resolve( $rootScope.i18n );
}
return this.existingPromise.promise;
};
The idea is that the user would call ensureLocaleIsLoaded and wait for the promise to be resolved. But given that the purpose of the function is to only ensure that the locale is loaded, it would be perfectly fine for the user to invoke it several times.
I'm currently just storing a single promise and resolve it if the user calls the function again after the locale has been successfully retrieved from the server.
From what I can tell, this is working as intended, but I'm wondering if this is a proper approach.
As I understand promises at present, this should be 100% fine. The only thing to understand is that once resolved (or rejected), that is it for a defered object - it is done.
If you call then(...) on its promise again, you immediately get the (first) resolved/rejected result.
Additional calls to resolve() will not have any effect.
Below is an executable snippet that covers those use cases:
var p = new Promise((resolve, reject) => {
resolve(1);
reject(2);
resolve(3);
});
p.then(x => console.log('resolved to ' + x))
.catch(x => console.log('never called ' + x));
p.then(x => console.log('one more ' + x));
p.then(x => console.log('two more ' + x));
p.then(x => console.log('three more ' + x));
I faced the same thing a while ago, indeed a promise can be only resolved once, another tries will do nothing (no error, no warning, no then invocation).
I decided to work it around like this:
getUsers(users => showThem(users));
getUsers(callback){
callback(getCachedUsers())
api.getUsers().then(users => callback(users))
}
just pass your function as a callback and invoke it as many times you wish! Hope that makes sense.
There s no clear way to resolve promises multiple times because since it's resolved it's done. The better approach here is to use observer-observable pattern for example i wrote following code that observes socket client event. You can extend this code to met your need
const evokeObjectMethodWithArgs = (methodName, args) => (src) => src[methodName].apply(null, args);
const hasMethodName = (name) => (target = {}) => typeof target[name] === 'function';
const Observable = function (fn) {
const subscribers = [];
this.subscribe = subscribers.push.bind(subscribers);
const observer = {
next: (...args) => subscribers.filter(hasMethodName('next')).forEach(evokeObjectMethodWithArgs('next', args))
};
setTimeout(() => {
try {
fn(observer);
} catch (e) {
subscribers.filter(hasMethodName('error')).forEach(evokeObjectMethodWithArgs('error', e));
}
});
};
const fromEvent = (target, eventName) => new Observable((obs) => target.on(eventName, obs.next));
fromEvent(client, 'document:save').subscribe({
async next(document, docName) {
await writeFilePromise(resolve(dataDir, `${docName}`), document);
client.emit('document:save', document);
}
});
If you need to change the return value of promise, simply return new value in then and chain next then/catch on it
var p1 = new Promise((resolve, reject) => { resolve(1) });
var p2 = p1.then(v => {
console.log("First then, value is", v);
return 2;
});
p2.then(v => {
console.log("Second then, value is", v);
});
You can write tests to confirm the behavior.
By running the following test you can conclude that
The resolve()/reject() call never throw error.
Once settled (rejected), the resolved value (rejected error) will be preserved
regardless of following resolve() or reject() calls.
You can also check my blog post for details.
/* eslint-disable prefer-promise-reject-errors */
const flipPromise = require('flip-promise').default
describe('promise', () => {
test('error catch with resolve', () => new Promise(async (rs, rj) => {
const getPromise = () => new Promise(resolve => {
try {
resolve()
} catch (err) {
rj('error caught in unexpected location')
}
})
try {
await getPromise()
throw new Error('error thrown out side')
} catch (e) {
rs('error caught in expected location')
}
}))
test('error catch with reject', () => new Promise(async (rs, rj) => {
const getPromise = () => new Promise((_resolve, reject) => {
try {
reject()
} catch (err) {
rj('error caught in unexpected location')
}
})
try {
await getPromise()
} catch (e) {
try {
throw new Error('error thrown out side')
} catch (e){
rs('error caught in expected location')
}
}
}))
test('await multiple times resolved promise', async () => {
const pr = Promise.resolve(1)
expect(await pr).toBe(1)
expect(await pr).toBe(1)
})
test('await multiple times rejected promise', async () => {
const pr = Promise.reject(1)
expect(await flipPromise(pr)).toBe(1)
expect(await flipPromise(pr)).toBe(1)
})
test('resolve multiple times', async () => {
const pr = new Promise(resolve => {
resolve(1)
resolve(2)
resolve(3)
})
expect(await pr).toBe(1)
})
test('resolve then reject', async () => {
const pr = new Promise((resolve, reject) => {
resolve(1)
resolve(2)
resolve(3)
reject(4)
})
expect(await pr).toBe(1)
})
test('reject multiple times', async () => {
const pr = new Promise((_resolve, reject) => {
reject(1)
reject(2)
reject(3)
})
expect(await flipPromise(pr)).toBe(1)
})
test('reject then resolve', async () => {
const pr = new Promise((resolve, reject) => {
reject(1)
reject(2)
reject(3)
resolve(4)
})
expect(await flipPromise(pr)).toBe(1)
})
test('constructor is not async', async () => {
let val
let val1
const pr = new Promise(resolve => {
val = 1
setTimeout(() => {
resolve()
val1 = 2
})
})
expect(val).toBe(1)
expect(val1).toBeUndefined()
await pr
expect(val).toBe(1)
expect(val1).toBe(2)
})
})
What you should do is put an ng-if on your main ng-outlet and show a loading spinner instead. Once your locale is loaded the you show the outlet and let the component hierarchy render. This way all of your application can assume that the locale is loaded and no checks are necessary.
No. It is not safe to resolve/reject promise multiple times. It is basically a bug, that is hard to catch, becasue it can be not always reproducible.
There is pattern that can be used to trace such issues in debug time. Great lecture on this topic: Ruben Bridgewater — Error handling: doing it right! (the part related to the question is around 40 min)
see github gist: reuse_promise.js
/*
reuse a promise for multiple resolve()s since promises only resolve once and then never again
*/
import React, { useEffect, useState } from 'react'
export default () => {
const [somePromise, setSomePromise] = useState(promiseCreator())
useEffect(() => {
somePromise.then(data => {
// do things here
setSomePromise(promiseCreator())
})
}, [somePromise])
}
const promiseCreator = () => {
return new Promise((resolve, reject) => {
// do things
resolve(/*data*/)
})
}

Categories