What's the difference between resolve(thenable) and resolve('non-thenable-object')? - javascript

I was trying to understand the difference between resolve(thenable) and resolve('non-thenable-object').
In examples below, use promise instead of thenable because promise is also thenable and may be easier to understand.
Demo1: resolve(promise)
let resolvePromise = new Promise(resolve => {
let resolvedPromise = Promise.resolve()
resolve(resolvedPromise)
})
resolvePromise.then(() => {
console.log('resolvePromise resolved')
})
let resolvedPromiseThen = Promise.resolve().then(res => {
console.log('promise1')
})
resolvedPromiseThen
.then(() => {
console.log('promise2')
})
.then(() => {
console.log('promise3')
})
result:
promise1
promise2
resolvePromise resolved
promise3
Demo2: resolve('non-thenable-object')
let resolvePromise = new Promise(resolve => {
resolve('non-thenable-object')
})
resolvePromise.then(() => {
console.log('resolvePromise resolved')
})
let resolvedPromiseThen = Promise.resolve().then(res => {
console.log('promise1')
})
resolvedPromiseThen
.then(() => {
console.log('promise2')
})
.then(() => {
console.log('promise3')
})
result:
resolvePromise resolved
promise1
promise2
promise3
So, I checked the spec and found Promise Resolve Functions
. Then got to PromiseResolveThenableJob and EnqueueJob.
So, according to the spec, I think demo1 was like
Demo3:
let resolvePromise = new Promise(resolve => {
let resolvedPromise = Promise.resolve()
// resolve(resolvedPromise)
// works like
Promise.resolve().then(() => {
Promise.resolve(resolvedPromise).then(() => {
resolve()
})
})
})
resolvePromise.then(() => {
console.log('resolvePromise resolved')
})
let resolvedPromiseThen = Promise.resolve().then(res => {
console.log('promise1')
})
resolvedPromiseThen
.then(() => {
console.log('promise2')
})
.then(() => {
console.log('promise3')
})
I think so because Promise Resolve Functions says:
Perform EnqueueJob("PromiseJobs", PromiseResolveThenableJob, « promise, resolution, thenAction »).
And PromiseResolveThenableJob says:
This Job uses the supplied thenable and its then method to resolve the given promise. This process must take place as a Job to ensure that the evaluation of the then method occurs after evaluation of any surrounding code has completed.
Also, I think demo2 works like
Demo4:
//let resolvePromise = new Promise(resolve => {
//resolve('str')
//})
//works like
let resolvePromise = Promise.resolve('str')
resolvePromise.then(() => {
console.log('resolvePromise resolved')
})
let resolvedPromiseThen = Promise.resolve().then(res => {
console.log('promise1')
})
resolvedPromiseThen
.then(() => {
console.log('promise2')
})
.then(() => {
console.log('promise3')
})
As the Promise Resolve Functions says:
If IsCallable(thenAction) is false, then Return FulfillPromise(promise, resolution).
Though the results between Demo1-Demo3 and Demo2-Demo4 are equal, I am still not sure if I was right. So, I am here to ask
whether my logic is right? If not, how do you explain the different orders
between resolve(thenable) and resolve(non-thenable) ?

Yes, your logic looks right.
new Promise(resolve => resolve('non-thenable-object')) is equivalent to Promise.resolve('non-thenable-object') for all purposes.
In your Demo3 I would however recommend to leave out Promise.resolve(resolvedPromise). I'm not sure whether that was intentional or not, but Promise.resolve does have a shortcut when its argument is already a promise, and then returns the resolvedPromise as-is. You'd rather write
new Promise((resolve, reject) => {
let resolvedPromise = Promise.resolve();
// resolve(resolvedPromise) works like
Promise.resolve().then(() => resolvedPromise.then(resolve, reject));
});

After reading the specification and testing many times I thought I might get it.
Before we start, we have to settle something.
let's call it RESOLVE() when using resolve in Promise executor. For example, RESOLVE(thenable) means the code like:
new Promise((resolve,reject)=>{
resolve(thenable)
})
while resolve(thenable) means Promise.resolve(thenable)
Ok, let's begin.
Promise.resolve('non-thenable') and RESOLVE('non-thenable')
When we are using Promise.resolve('non-thenable') it comes to Promise.resolve
Then it comes to PromiseResolve
That's the where Promise.resolve('non-thenable') was transformed to
new Promise(resolve=>{
resolve('non-thenable')
})
So, we have the conclusion:
Promise.resolve('non-thenable') can be transformed into RESOLVE('non-thenable')
RESOLVE(thenable)
demo1
let resolveThenable = new Promise((resolve, reject) => {
let thenable = {
then: function (resolve, reject) {
console.log('in thenable')
resolve(42)
}
}
resolve(thenable)
// works like
// Promise.resolve().then(() => {
// thenable.then(resolve)
// })
// should be ?
// Promise.resolve().then(() => {
// thenable.then.[[Value]](resolve)
// })
// equivalent to?
// Promise.resolve().then(() => {
// thenable.then(resolve)
// })
})
resolveThenable.then(() => {
console.log('resolveThenable resolved')
})
let resolvedPromiseThen = Promise.resolve().then(res => {
console.log('promise1')
})
resolvedPromiseThen
.then(() => {
console.log('promise2')
})
.then(() => {
console.log('promise3')
})
// 'in thenable'
// 'promise1'
// 'resolveThenable resolved'
// 'promise2'
// 'promise3'
According to Promise Resolve Functions, when we were using RESOLVE(thenable) it comes to
Then it comes to PromiseResolveThenableJob
This would make RESOLVE(thenable) works like
Promise.resolve().then(() => {
thenable.then.[[Value]](resolve)
})
So, I thought it is equivalent to
Promise.resolve().then(() => {
thenable.then(resolve)
})
Which got the same result as RESOLVE(thenable).
So, we can say RESOLVE(thenable) can be transformed to
Promise.resolve().then(() => {
thenable.then(resolve)
})
demo2
let resolvePromise = new Promise((resolve, reject) => {
let resolvedPromise = Promise.resolve()
resolve(resolvedPromise)
// works like
// Promise.resolve().then(() => {
// resolvedPromise.then(() => {
// resolve()
// })
// })
// should be?
// Promise.resolve().then(() => {
// resolvedPromise.then.[[Value]](resolve,reject)
// })
// equivalent to ?
// Promise.resolve().then(() => {
// resolvedPromise.then(resolve)
// })
// equivalent to ?
// Promise.resolve().then(() => {
// resolvedPromise.then(() => {
// resolve()
// })
// })
})
resolvePromise.then(() => {
console.log('resolvePromise resolved')
})
let resolvedPromiseThen = Promise.resolve().then(res => {
console.log('promise1')
})
resolvedPromiseThen
.then(() => {
console.log('promise2')
})
.then(() => {
console.log('promise3')
})
// 'promise1'
// 'promise2'
// 'resolvePromise resolved'
// 'promise3'
When we talked about RESOLVE(resolvedPromise), we can find the spec doesn't distinguish thenable from promise. So, in the same way, RESOLVE(resolvedPromise) can be transformed into
Promise.resolve().then(() => {
resolvedPromise.then(resolve)
})
Though, in this case, the order between RESOLVE(thenable) and RESOLVE(promise) is different. Because thenable.then is a sync operation while resolvedPromise.then is an async operation. They are not the same then method.
So, here is our conclusion:
Both RESOLVE(thenable) and RESOLVE(promise) can be transformed into
new Promise((resolve, reject) => {
Promise.resolve().then(() => {
thenable.then(resolve)
})
})
Promise.resolve(thenable)
It is quite simple when using Promise.resolve(promise) because it returns promise argument.
However, things become complicated when using Promise.resolve(thenable) and the thenable is not a promise. Let's call it Promise.resolve(nonPromiseThenable).
According to Promise.resolve ( x )
Then it comes to
So, Promise.resolve(nonPromiseThenable) can be transformed to
new Promise(resolve => {
resolve(nonPromiseThenable)
})
And finally comes to
new Promise(resolve => {
Promise.resolve().then(() => {
nonPromiseThenable.then(resolve)
})
})
You can test it in the demo below.
var thenable = {
then(resolve, reject) {
resolve(1)
}
}
// code transformation
Promise.resolve(thenable).then(res => {
console.log(res)
})
// equal
// new Promise(resolve => {
// resolve(thenable)
// }).then(res => {
// console.log(res)
// })
// equal
// new Promise(resolve => {
// Promise.resolve().then(() => {
// thenable.then(resolve)
// })
// }).then(res => {
// console.log(res)
// })
new Promise(resolve => resolve(2))
.then(res => {
console.log(res)
})
.then(res => console.log(3))
In the end, let's make a conclusion:
Promise.resolve('nonThenable') can be transformed into RESOLVE('nonThenable'). They have the same effects.
Promise.resolve(thenable) is different from RESOLVE(thenable). They have different effects.
RESOLVE(thenable) and RESOLVE(promise) can be transformed into
new Promise((resolve, reject) => {
Promise.resolve().then(() => {
thenable.then(resolve)
})
})
Promise.resolve(promise) === promise while Promise.resolve(nonPromiseThenable) can be transformed into
new Promise(resolve => {
Promise.resolve().then(() => {
nonPromiseThenable.then(resolve)
})
})

Related

How to correctly resolve a promise within promise constructor

const setTimeoutProm = (delay) => new Promise(res => setTimeout(() => res(delay),delay))
I want to do something like,
const asyncOpr = (delay) => {
return new Promise((resolve, reject) => {
//update delay for some reason.
const updatedDelay = delay * 2;
setTimeoutProm(updatedDelay).then(res => {
resolve(res);
}).catch(err => {})
})
}
asyncOpr(2000).then(() => alert("resolved")) //this works
This works as expected, but I am not sure if this is correct way of doing this or is there any better way of doing this ?
No, actually the way you do it is an antipattern.
You can just return a promise from the function:
const asyncOpr = (delay) => {
return setTimeoutProm(delay);
};
If needed, a Promise could also be returned from inside a .then:
doA()
.then(() => setTineoutProm(1000))
.then(() => doB());
Or it can also be awaited inside an async function:
async function asyncOpr(delay) {
//...
await setTimeoutProm(delay);
//...
}

Promise .then() chains: second .then run before the first?? o.0

I'm trying to create a new File object from blob data in a .then structure, and after in the second .then, read info of this file.
But the second then run before the first end, so the file object isn't filled yet.
Is that a normal behavior? Should I make an async function, called in the first then to ensure the second one is strictly called after?
let output = {file: {}, file_infos: {}},
image = FileAPI.Image(src_file);
await Promise.all(Object.keys(parameters).map(async (parameter_name) => { // Pass file threw all modifiers (resizing, rotation, overlaying)
try {
image = await FileMethods[parameter_name](image, parameters[parameter_name]);
return image;
}
catch(err) {
console.log(err);
};
}))
.then((output_image) => {
output_image[0].toBlob((blob) => {
output.file = new File([blob], src_file.name); // Need this to be fullfilled before step2
console.log('step1');
});
})
.then(() => {
console.log('step2');
FileAPI.getInfo(output.file, (err/**String*/, infos/**Object*/) => {
if( !err ){
output.file_infos = infos;
} else {
console.log("this is triggered because output.file isn't filled yet");
}
})
});
// console.log(output);
return output;
console shows me:
step2
this is triggered because output.file isn't filled yet
step1
Thanks for helps :)
The two asynchronous functions in the two .then's do not return a Promise, so first they need to be "Promisified", also, since you're already using async/await don't use a promise .then chain
const image = FileAPI.Image(src_file);
const output_image = await Promise.all(Object.keys(parameters).map(async(parameter_name) => {
try {
image = await FileMethods[parameter_name](image, parameters[parameter_name]);
return image;
} catch (err) {
console.log(err);
};
}));
const file = await new Promise((resolve, reject) => output_image[0].toBlob((blob) =>
resolve(new File([blob], src_file.name))
));
const file_infos = await new Promise((resolve, reject) => FileAPI.getInfo(file, (err, file_infos) => {
if (!err) {
resolve(file_infos);
} else {
reject("this is triggered because output.file isn't filled yet");
}
));
return {file, file_infos};
A note about
const output_image = await Promise.all(Object.keys(parameters).map(async(parameter_name) => {
try {
image = await FileMethods[parameter_name](image, parameters[parameter_name]);
return image;
} catch (err) {
console.log(err);
};
}));
you're essentially doing return await FileMethods[parameter_name](image, parameters[parameter_name]) - so, you really don't need in this case an async/await pattern, just return the Promise in the .map
const output_image = await Promise.all(Object.keys(parameters).map((parameter_name) =>
FileMethods[parameter_name](image, parameters[parameter_name]);
));
Or, even nicer (in my opinion)
const output_image = await Promise.all(Object.entries(parameters).map((p_name, p_value) =>
FileMethods[p_name](image, p_value)
));
Alternatively, to use Promise .then chains and no async/await
const image = FileAPI.Image(src_file);
return Promise.all(Object.keys(parameters).map(parameter_name => FileMethods[parameter_name](image, parameters[parameter_name])))
.then(output_image => new Promise((resolve, reject) => output_image[0].toBlob((blob) =>
resolve(new File([blob], src_file.name))
)))
.then(file => new Promise((resolve, reject) => FileAPI.getInfo(file, (err, file_infos) => {
if (!err) {
resolve({file, file_infos});
} else {
reject("this is triggered because output.file isn't filled yet");
}
)));
.toBlob() returns instantly because it uses the asynchronous callback pattern.
What you want is to return a promise that resolves when the work is done. So you could do something like this:
.then((output_image) => {
return new Promise((res, rej) => {
output_image[0].toBlob((blob) => {
output.file = new File([blob], src_file.name); // Need this to be fullfilled before step2
console.log('step1');
res();
});
});
})
toBlob is probably asynchronous. Change the first .then to this:
.then((output_image) => {
return new Promise((resolve) => output_image[0].toBlob((blob) => {
output.file = new File([blob], src_file.name); // Need this to be fullfilled before step2
console.log('step1');
resolve();
}));
})

Promise.reject() vs return promise.reject()

I have been trying to understand the difference between the following two and which is the idle way to use:
let getClient = () => {
return connect()
.then((client) => {
return Promise.resolve(client);
})
.catch((err) => {
return Promise.reject(err);
}
}
and
let getClient = () => {
return connect()
.then((client) => {
Promise.resolve(client);
})
.catch((err) => {
Promise.reject(err);
}
}
and
let getClient = () => {
return new Promise((resolve, reject) => {
return connect()
.then((client) => {
resolve(client);
})
.catch((err) => {
reject(err);
})
})
}
can someone help me understand the difference? Does return Promise.resove/reject make a difference to just using Promise.resolve/reject ?
They are all poor examples.
connect() is thenable, so it presumably returns a promise. Creating extra promises that do nothing except return the results of another promise just over complicates things.
You could rewrite the whole thing as:
let getClient = connect;
… and get something that is more-or-less identical (unless you were then going to go and apply some weird edge cases).
Your first example takes the results of resolving the connect promise, creates a new promise and immediately resolves it with the same value, then returns that promise which is adopted by the connect promise.
Your second example does the same thing, except without the adoption, so the original connect promise results are available in the next then in the chain. (These results are identical to the ones that stepped through the extra promise in the previous example).
Your third example creates a new promise outside the call to connect, and then resolves that with the value from connect. It's another pointless extra promise.
A good question. In your example, first and last snippet yields the same result. But the second one will give you undefined as you are not returning a promise. Also the time taken/sequence of operation differs.
const connect = (input) => new Promise((res, rej) => (!!input ? res('Success') : rej('Failure')));
let getClient_0 = (input) => {
return connect(input)
.then((client) => {
return Promise.resolve(client);
})
.catch((err) => {
return Promise.reject(err);
})
}
let getClient_1 = (input) => {
return connect(input)
.then((client) => {
Promise.resolve(client);
})
.catch((err) => {
Promise.reject(err);
})
}
let getClient_2 = (input) => {
return new Promise((resolve, reject) => {
return connect(input)
.then((client) => {
resolve(client);
})
.catch((err) => {
reject(err);
})
})
}
getClient_0(1).then((r) => console.log('getClient_0 -> ',r)).catch(e => console.log('getClient_0 -> ',e));
getClient_0(0).then((r) => console.log('getClient_0 -> ',r)).catch(e => console.log('getClient_0 -> ',e));
getClient_1(1).then((r) => console.log('getClient_1 -> ',r)).catch(e => console.log('getClient_1 -> ',e));
getClient_1(0).then((r) => console.log('getClient_1 -> ',r)).catch(e => console.log('getClient_1 -> ',e));
getClient_2(1).then((r) => console.log('getClient_2 -> ',r)).catch(e => console.log('getClient_2 -> ',e));
getClient_2(0).then((r) => console.log('getClient_2 -> ',r)).catch(e => console.log('getClient_2 -> ',e));

how can I catch the exception when I use promise.all[] or stop the chain call

I'm a little confused about the usage of Promise.
Please see the examples below:
Q1. How can I stop the chain call?
const wait = (duration = 0) => {
return new Promise((resolve) => {
setTimeout(resolve, duration);
});
};
const asyncTask1 = () => {
return wait(1000).then(() => {
console.log('task1 takes 1s.');
});
};
const asyncTask2 = () => {
return wait(3000).then(() => {
console.log('task2 takes 3s.');
});
};
Promise.resolve()
.then(() => {
asyncTask1();
})
.then(() => {
asyncTask2();
})
.catch(() => {
console.log('fail.');
});
As the code shows, asyncTask2 won't run until asyncTask1 has completed. But the question is how can I stop the chain call if asyncTask1 fails. Namely asyncTask2 won't run because the asyncTask1 fails
.
Note: no error or exception generated in asyncTask1, I want to use a status (success or failed) to decide the result of asyncTask1.
Q2. How can I know which task generates the exception when I use Promise.all()?
const wait = (duration=0) => {
return new Promise((resolve) => {
setTimeout(resolve, duration);
});
};
const asyncTask1 = () => {
return wait(1000).then(() => {
console.log('task1 takes 1s.');
});
};
const asyncTask2 = () => {
return wait(3000).then(() => {
console.log('task2 takes 3s.');
});
};
Promise
.all([asyncTask2(), asyncTask1()])
.then(() => {
console.log('all done.');
})
.catch((e) => {
console.log('error');
});
The code above can run successfully, but if asyncTask1 or asyncTask2 fails, it will go into the catch function. So the question is how can I know whether the exception is from asyncTask1 or asyncTask2?
You can use Array.prototype.map() to call the functions set at an array, throw the index of the element where error occurs within .map() callback at .catch()
const wait = (duration=0) => {
return new Promise((resolve) => {
setTimeout(resolve, duration);
});
};
const asyncTask1 = () => {
return wait(1000).then(() => {
console.log('task1 takes 1s.');
});
};
const asyncTask2 = () => {
return wait(3000).then(() => {
throw new Error()
console.log('task2 takes 3s.');
});
};
let arr = [asyncTask2, asyncTask1];
Promise
.all(arr.map((p, index) =>
p().catch(err => {throw new Error(err + " at index " + index)})))
.then(() => {
console.log('all done.');
})
.catch((e) => {
console.log('error', e);
});

Wait for promises inside Promise.all to finish before resolving it

I have a Promise.all that executes asynchronous functions mapped on an array input if it's not null and then resolve data to a previously defined Promise:
Promise.all((inputs || []).map(input => {
return new Promise((resolve, reject) => {
someAsyncFunc(input)
.then(intermediateOutput => {
someOtherAsyncFunc(intermediateOutput )
.then(output => {
return Promise.resolve(output )
})
.catch(reason=> {
return Promise.reject(reason)
})
})
.catch(reason => {
return Promise.reject(reason);
})
})
.then(outputs => {
resolve(outputs)
})
.catch(reason => {
reject(reason)
})
}))
I only get empty outputs before even someAsyncFunc finishes its work. How can make Promise.all wait for the promises inside to finish their asynchronous work ?
Would not just
return Promise.all((inputs || []).map(input =>
somePromiseFunc(input).then(someOtherPromiseFunc)
);
work ?
You're not using Promise.all right the first time since it takes an array of promises as input, and not (resolve, reject) => { ... }
Promise.all is going to be rejected as soon as one of the underlying promises fails, so you don't need to try to do something around catch(error => reject(error)
Example:
const somePromiseFunc = (input) => new Promise((resolve, reject) => {
setTimeout(() => {
if (input === 0) { reject(new Error('input is 0')); }
resolve(input + 1);
}, 1000);
});
const someOtherPromiseFunc = (intermediateOutput) => new Promise((resolve, reject) => {
setTimeout(() => {
if (intermediateOutput === 0) { reject(new Error('intermediateOutput is 0')); }
resolve(intermediateOutput + 1);
}, 1000);
});
const f = inputs => {
const t0 = Date.now()
return Promise.all((inputs || []).map(input => somePromiseFunc(input).then(someOtherPromiseFunc)))
.then(res => console.log(`result: ${JSON.stringify(res)} (after ${Date.now() - t0}ms)`))
.catch(e => console.log(`error: ${e} (after ${Date.now() - t0}ms)`));
};
f(null)
// result: [] (after 0ms)
f([1, 0])
// error: Error: input is 0 (after 1001ms)
f([1, -1])
// error: Error: intermediateOutput is 0 (after 2002ms)
f([1, 2])
// result: [3,4] (after 2002ms)
See jfriend's comment.
someAsyncFunc and someOtherAsyncFunc are function that properly return a promise
with something like return new Promise(/*...*/);
this is useless:
.then(output => {
return Promise.resolve(output )
})
read the Promise documentation
same
.catch(reason=> {
return Promise.reject(reason)
})
the Promise is already rejecting, you don't need to catch and reject yourself
to make sure Promises are chainable you need to return the Promise
// ...
return new Promise((resolve, reject) => {
if(inputs == null)
resolve([]);
else {
Promise.all(inputs.map(input => {
return someAsyncFunc(input)
.then(someOtherAsyncFunc)
}))
.then(resolve)
.catch(reject)
}
});
note I would rather not make the arry for Promise.all inline, it adds visual clutter:
return new Promise((resolve, reject) => {
if(inputs == null)
resolve([]);
else {
const myPromises = inputs.map(input => {
return someAsyncFunc(input)
.then(someOtherAsyncFunc)
});
Promise.all(myPromises)
.then(resolve)
.catch(reject)
}
});
it may still fail if you made other mistakes.

Categories

Resources