JavaScript:Promise
lastmod 2022-08-30created 2021-07-1610 edits
A Promise is an object representing the eventual completion or failure of an asynchronous operation.
APIs
race
Returns the Promise that finishes first.
reject
The Promise.reject(reason) method returns a Promise object that is rejected with the given reason (reason).
Example:
Promise.reject("Testing static reject").then(function(reason) {
// 호출되지 않음
}, function(reason) {
console.log(reason); // "Testing static reject"
});
Promise.reject(new Error("fail")).then(function(error) {
// 호출되지 않음
}, function(error) {
console.log(error); // Stacktrace
});
Creating a Promise around an old callback API
A Promise can be created from scratch using its constructor. This should be needed only to wrap old APIs.
In an ideal world, all asynchronous functions would already return promises. Unfortunately, some APIs still expect success and/or failure callbacks to be passed in the old way. An example is the setTimeout() function.
Mixing old-style callbacks and Promises is problematic. If saySomething() fails or contains a programming error, nothing catches it. This is a problem with setTimeout().
Luckily, we can wrap setTimeout() in a Promise. Best practice is to wrap problematic functions at the lowest possible level, and then never call them directly again.
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
wait(10000).then(() => saySomething("10 seconds")).catch(failureCallback);
Basically, the promise constructor takes an executor function that lets us resolve or reject a promise manually. Since setTimeout() doesn't really fail or throw an error, reject is not used in this case.
Creating a Promise timeout
In short, proceed in the following order:
- Turn the original task into a Promise.
- Create a Promise that throws an exception from setTimeout.
- Call the two Promises above with the #race function.
waitInitialized(timeout?: number) {
const initPromise = this._initPromise.then((value: boolean) => {
if (!value) {
throw new Error();
}
return this.api;
});
if (typeof timeout !== 'undefined') {
return initPromise;
}
const timeoutPromise = new Promise((resolve, reject) => {
setTimeout(() => {
reject(new TimeoutError('An initialization timeout occurred'));
}, timeout);
});
return Promise.race([initPromise, timeoutPromise]);
}
Nested Promises
Testing the following code with Jest ...
describe('playground', () => {
test('default', async () => {
console.debug('BEGIN --');
const aaa = new Promise((resolve, reject) => {
setTimeout(() => {
console.debug('resolve - before');
resolve(1);
console.debug('resolve - after');
}, 1000);
})
.then(() => {
console.debug('timeout done.');
})
.then(async () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.debug('[INSIDE] reject - before');
reject('[INSIDE] reject call !');
console.debug('[INSIDE] reject - after');
}, 1000);
})
.then(() => {
console.debug('[INSIDE] timeout done.');
})
.catch(error => {
console.error(error);
});
})
.then(() => {
console.debug('next then');
})
.catch(error => {
console.error(error);
});
console.debug('Promise.all ...');
await Promise.all([aaa]);
console.debug('END --');
});
});
produces the following result.
BEGIN --
Promise.all ...
resolve - before
resolve - after
timeout done.
[INSIDE] reject - before
[INSIDE] reject - after
[INSIDE] reject call !
next then
END --
Conclusion: nested Promises produce the expected output. In particular, even using the async function on line 14 works fine, since it just ends up as another level of Promise nesting.
See also
Favorite site
- Using promises
- [Recommended] Graceful asynchronous programming with Promises - Learn web development | MDN
- Understanding JavaScript Promises easily • Captain Pangyo