How to understand this Promise code? - javascript

'use strict';
Promise.resolve(() => 'John')
.then((args) => {
console.log(args);
throw new Error('ops')
})
.catch((ex) => {
console.log(ex)
})
.then(() => {
throw new Error('ups')
console.log('Doe')
})
I think console.log(args); should output 'John', but when I run this code, the output is [ [Function] ]
So I am confused.

Promise.resolve will create a new Promise resolved with the value you pass to it. So, in your case, your promise is actually resolved with the function object. It means that, the then handler is passed the function object itself.
What you should have done is
new Promise((resolve, reject) => resolve('John'))
.then(args => {
console.log(args);
throw new Error('ops')
})
.catch(console.log.bind(console));
Now, you are creating a Promise object and you are resolving that with the value John.
If you want your Promise to be resolved with a value readily, then don't pass the function object but pass the actual value itself to Promise.resolve function.
Promise.resolve('John')
.then(args => {
console.log(args);
throw new Error('ops')
})
.catch(console.log.bind(console));
Now, you have a Promise, resolved with value John and the then handler will get the resolved value John.
Note: This is the recommended way of creating a Promise when you know the actual way to resolve with readily, so that you can avoid the Promise constructor anti-pattern.

'use strict';
Promise.resolve('John')
.then((args) => {
console.log(args);
throw new Error('ops')
})
.catch((ex) => {
console.log(ex)
})
.then(() => {
throw new Error('ups')
console.log('Doe')
})
I modify Promise.resolve('John'), it works.
Please see the Promise.resolve.

resolve is used to pass the arguments directly to the then-handler
if you want 'John', you need to call the anonymous function in your call to resolve()
Promise.resolve(function(){return 'John';}());
notice the }() function call.

Related

Typescript - Promise returns to early

I'm given a code with 3 parts that must not be changed:
1. a call to getData function, and then prints the output.
getData().then(console.log);
2. The function signeture:
async getData(): Promise<string[]>
3. function getDataFromUrl
function getDataFromUrl(url: string, callback: any) {
fetch(URL)
.then((content: any) => content.json())
.then((data) => callback(data));
}
This is my implementation for getData function:
async getData(): Promise<string[]> {
return await new Promise<any>(function(resolve, reject) {
resolve(getDataFromUrl(myUrl, ((data: string[]): string[]=> {
return data
})))
});
}
The problem is that the code after fetch, runs after
getData().then(console.log);
so console.log prints: undefined
What do I need to change in the getData function?
Thanks
Instead of resolving the getDataFromUrl function, you should resolve the value that the callback function exposes.
getData(): Promise<string[]> {
return new Promise<string[]>(function(resolve, reject) {
getDataFromUrl(myUrl, (data: string[]): string[] => {
resolve(data)
})
});
}
It's unfortunate you can't change anything about the other functions, because returning the promise created by fetch in your getDataFromUrl function, would make the code way better, as Quentin demonstrates.
getDataFromUrl doesn't have a return value, so it returns undefined.
So you pass undefined to resolve (which resolves your other promise) and then, later the promise created by getDataFromUrl (or rather fetch) resolves.
Don't create bonus promises
Don't mix callbacks and promises
Just return the promise from fetch.
function getDataFromUrl(url: string, callback: any) {
return fetch(URL)
.then((content: any) => content.json());
}
And use that promise instead of creating a new one in getData (which doesn't need to use await because that will just resolve the promise which will immediately get wrapped in a new promise by async.
getData(): Promise<string[]> {
return getDataFromUrl(myUrl);
}
function getDataFromUrl(url, f) {
// call fetch (returns promise) but we can use then as well.
// note our callback: f where we pass the data to the callback.
fetch(url).then((content) => content.json()).then(data => f(data));
}
// edit: note we don't need this be async anymore...
async function getData() {
// create a promise...
return new Promise((resolve, reject) => {
// we call with callback arrow and resolve data here
getDataFromUrl('https://randomuser.me/api/', (data) => resolve(data));
})
}
// we print the data once the getData is done fetching
// you can also use reject to handle anything that goes wrong in above.
getData().then(data => console.log(data));
Resources used:
Fetch API
Async, Await, and Promise
Callback

JS Promise <Pending> returned

I'm currently struggling with returning a value from a chained event resulting in a Promise returned in a pending state.
My code returns an array of metafield objects for a specific Shopify product, which I then parse and would like to return an integer value.
However when I debug my code all I'm getting is a promise in a pending state rather than an executed value which is in scope to use.
I'm guessing this is largely down to my unfamiliarity with promises.
Any help greatly appreciated!
var quantity = shopify.metafield.list({
metafield: { owner_resource: 'product', owner_id: line_item.product_id }
}).then(metafields => new Promise(function(resolve, reject) {
//simplified code
resolve(2);
}))
.catch(error => console.log(error));
shopify.metafield.list({
metafield: { owner_resource: 'product', owner_id: line_item.product_id }
}).then(metafields => new Promise(function(resolve, reject) {
//simplified code
resolve(2);
})).then((quantity)=> console.log(quantity))
.catch(error => console.log(error));
2nd .then() function is returning a new promise so you need to chain .then() function again in order to get the exact value returned from the promise(resolve)
Other way is to just use .then() function on quantity variable which contains promise returned by 2nd .then() function
quantity.then((res)=> console.log(res))

Why does returning Promise.resolve() inside `then` cause two promise creation instead of one?

Actually, I don't know how to check this on real Promise super class, so I'm testing it with a class extending it. But it seems to me the result would be the same.
class PromisePlus extends Promise {
constructor (handler) {
super(handler)
console.log('new promise created')
}
}
new PromisePlus((resolve, reject) => {
resolve()
}).then(() => {
return 'GOOD'
}).then(msg => {
console.log(msg)
})
prints 'new promise created' 3 times
new PromisePlus((resolve, reject) => {
resolve()
}).then(() => {
return PromisePlus.resolve('BAD')
}).then(msg => {
console.log(msg)
})
prints 'new promise created' 5 times.
The same result with returning new Promise inside then handler
new PromisePlus((resolve, reject) => {
resolve()
}).then(() => {
return new PromisePlus((resolve, reject) => {
resolve('BAD')
})
}).then(msg => {
console.log(msg)
})
Obviously, PromisePlus.resolve() creates new promise, but why does returning PromisePlus.resolve() inside then cause one more extra promise creation?
Why does returning PromisePlus.resolve() inside then cause one more extra promise creation?
Because when you are returning a promise from a then callback, it does get awaited to resolve the outer promise (the one returned from .then()). To await this inner promise, .then(…) gets called on it - and this call does create a new promise as well.
(Yes, all this is horribly inefficient, which is probably the #1 reason why promise subclassing should be avoided).

Optional catch in javascript promises

The following is valid:
new Promise<void>((resolve, reject) => {
reject()
})
.then(() => {})
.catch(() => {})
But I might not always care about the error. Is there a way to make the catch optional?
I tried this but it didn't work:
new Promise<void>((resolve, reject?) => {
if (reject) reject()
})
.then(() => {})
Error: Uncaught (in promise): undefined
Is there a way to make the catch optional?
No. If you are using a promise that might error, you need to handle that (or propagate it to your caller).
Of course if you create the promise yourself, rejecting it is optional, and you can choose to never reject your promises so that you won't need to handle any errors. But if there are errors from promises that you are using, and you want to ignore them, you must do so explicitly. Just write
somePromise.catch(e => void e);
// or () => { /* ignore */ }
// or function ignore() {}
I was trying to solve the same issue, and finally come up with the following promise wrapper:
/**
* wraps a given promise in a new promise with a default onRejected function,
* that handles the promise rejection if not other onRejected handler is provided.
*
* #param customPromise Promise to wrap
* #param defaultOnRejected Default onRejected function
* #returns wrapped promise
*/
export function promiseWithDefaultOnRejected(customPromise: Promise<any>, defaultOnRejected: (_: any) => any): Promise<any> {
let hasCatch = false;
function chain(promise: Promise<any>) {
const newPromise: Promise<any> = new Promise((res, rej) => {
return promise.then(
res,
function(value) {
if (hasCatch) {
rej(value);
} else {
defaultOnRejected(value);
}
},
);
});
const originalThen = newPromise.then;
// Using `defineProperty` to not overwrite `Promise.prototype.then`
Object.defineProperty(newPromise, 'then', {
value: function (onfulfilled: any, onrejected: any) {
const result: Promise<any> = originalThen.call(newPromise, onfulfilled, onrejected);
if (typeof onrejected === 'function') {
hasCatch = true;
return result;
} else {
return chain(result);
}
}
});
return newPromise;
}
return chain(customPromise);
}
This function lets you wrap your promises with a defaultOnRejected function that will handle the rejected promise if no other handler is provided. For example:
const dontCare = promiseWithDefaultOnRejected(Promise.reject("ignored"), () => {});
The result promise will never throw an "Unhandled Promise Rejection", and you can use it as follows:
dontCare.then(x=>console.log("never happens")).catch(x=>console.log("happens"));
or
dontCare.then(x=>console.log("never happens"), x=>console.log("happens"));
or simply without onRejected handler:
dontCare.then(x=>console.log("never happens")).then(x=>console.log("also never happens"));
An issue with this util is that it is not working as expected with async/await syntax: you need to propagate and handle the "catch" path as follows:
async () => {
try {
await promiseWithDefaultOnRejected(Promise.reject("ignored"), () => {})
.catch((e) => { throw e; });
} catch (e) {
console.log("happens");
}
}
You could resolve when the error is something you don't care about. If your catch returns anything other than a rejected promise, the error isn't propagated down the chain.
const ignorableError = new Error("I don't care about this error");
const myPromise = new Promise((resolve, reject) => {
reject(ignorableError);
})
.then(() => {})
.catch(error => {
if(error == ignorableError) {
console.log("Error ignored");
return;
}
// Do something else...
});
myPromise.then(() => console.log("Success"))
Let me try to describe your situation:
You have a service that gets user information and a function called getUser that uses that service. When the service fails for any reason then getUser does not have a user available. The result of getUser is used quite a lot of times in your code with the following situation(s):
User is available run a function (block of code).
User is not available run a function (block of code).
Run a function with the error/reject of the service.
When using getUser result you may want to run all 3 functions, a combination of 2 of them or only one.
Current getUser returns a Promise and this type does not seem to be suitable for your situation. Mainly because rejecting a promise and not catching it will cause unhandled promise rejection. And because if you want to run code if user is available or not it complicates the functions (they both have to check the result instead of assuming user is or is not available).
Maybe you can try the following code, please be careful making assumptions in the not available block, this could be due to any error. For example: it does not mean the user does not exist because it could be a network error.
In the example getUser is used but can be any function that returns a promise where you assume not available on reject.
const isAvailable = promise => {
//do not expose NOT_AVAILABLE outside this function
const NOT_AVAILABLE = {type:"not available"};
//no uncaught promise rejection errors by VM
const savePromise = promise.catch(x=>x);
return {
available:fn=>
promise
.catch(e=>Promise.reject(NOT_AVAILABLE))
.then(fn)
.catch(
e=>
(e===NOT_AVAILABLE)
? undefined//ignore
: Promise.reject(e)//re throw, error is not from service
),
//call not available with the promise if promise rejects
notAvailable:fn=>promise.catch(()=>fn(promise)),
catchError:promise.catch.bind(promise)
};
}
const service = arg =>
(arg===1)
? Promise.resolve(arg)
: Promise.reject(arg)
const getUser = arg => isAvailable(service(arg));
var user = getUser(2);
//if service failed available will be skipped
user.available(
user=>console.log("skipped:",user)
);
//both catch and notAvailable will be called
user.notAvailable(
arg=>console.log("not available:",arg)
);
user.notAvailable(
arg=>console.log("still not available:",arg)
);
//not handling catchError does not cause uncaught promise exception
// but you can inspect the error
// user.catchError(
// err=>console.log("error is::",err)
// );
var user = getUser(1);
//you can call available on user multiple times
user.available(
user=>console.log("got user:",user)
);
user.available(
user=>Promise.resolve(user)
.then(user=>console.log("still got user:",user))
.then(x=>Promise.reject("have to catch this one though"))
.catch(e=>console.log("ok, got error trying to run available block:",e))
);
//showing you can inspect the error
var user = getUser(5);
user.catchError(err=>console.log("error from service:",err));

Promise chaining, chain promise object

I would like to chain promise object, but the problem that the second then block is executed before the chained promise object got resolved.
service.webService1()
.then(data => {
if (data.flag) { // true
return service.webService2() // returns a promise
}
return $q.resolve({})
})
.then(data => {
// some logic
// data is undefined here, should be the result of
// service.webService2() call
})
.catch(err => {
// err handling
})
The problem that I forgot to return my promise in the service.webService2() function. Thanks :)

Categories