Any difference between await Promise.all() and multiple await? - javascript

Is there any difference between:
const [result1, result2] = await Promise.all([task1(), task2()]);
and
const t1 = task1();
const t2 = task2();
const result1 = await t1;
const result2 = await t2;
and
const [t1, t2] = [task1(), task2()];
const [result1, result2] = [await t1, await t2];

Note:
This answer just covers the timing differences between await in series and Promise.all. Be sure to read #mikep's comprehensive answer that also covers the more important differences in error handling.
For the purposes of this answer I will be using some example methods:
res(ms) is a function that takes an integer of milliseconds and returns a promise that resolves after that many milliseconds.
rej(ms) is a function that takes an integer of milliseconds and returns a promise that rejects after that many milliseconds.
Calling res starts the timer. Using Promise.all to wait for a handful of delays will resolve after all the delays have finished, but remember they execute at the same time:
Example #1
const data = await Promise.all([res(3000), res(2000), res(1000)])
// ^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^
// delay 1 delay 2 delay 3
//
// ms ------1---------2---------3
// =============================O delay 1
// ===================O delay 2
// =========O delay 3
//
// =============================O Promise.all
async function example() {
const start = Date.now()
let i = 0
function res(n) {
const id = ++i
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
console.log(`res #${id} called after ${n} milliseconds`, Date.now() - start)
}, n)
})
}
const data = await Promise.all([res(3000), res(2000), res(1000)])
console.log(`Promise.all finished`, Date.now() - start)
}
example()
This means that Promise.all will resolve with the data from the inner promises after 3 seconds.
But, Promise.all has a "fail fast" behavior:
Example #2
const data = await Promise.all([res(3000), res(2000), rej(1000)])
// ^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^
// delay 1 delay 2 delay 3
//
// ms ------1---------2---------3
// =============================O delay 1
// ===================O delay 2
// =========X delay 3
//
// =========X Promise.all
async function example() {
const start = Date.now()
let i = 0
function res(n) {
const id = ++i
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
console.log(`res #${id} called after ${n} milliseconds`, Date.now() - start)
}, n)
})
}
function rej(n) {
const id = ++i
return new Promise((resolve, reject) => {
setTimeout(() => {
reject()
console.log(`rej #${id} called after ${n} milliseconds`, Date.now() - start)
}, n)
})
}
try {
const data = await Promise.all([res(3000), res(2000), rej(1000)])
} catch (error) {
console.log(`Promise.all finished`, Date.now() - start)
}
}
example()
If you use async-await instead, you will have to wait for each promise to resolve sequentially, which may not be as efficient:
Example #3
const delay1 = res(3000)
const delay2 = res(2000)
const delay3 = rej(1000)
const data1 = await delay1
const data2 = await delay2
const data3 = await delay3
// ms ------1---------2---------3
// =============================O delay 1
// ===================O delay 2
// =========X delay 3
//
// =============================X await
async function example() {
const start = Date.now()
let i = 0
function res(n) {
const id = ++i
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
console.log(`res #${id} called after ${n} milliseconds`, Date.now() - start)
}, n)
})
}
function rej(n) {
const id = ++i
return new Promise((resolve, reject) => {
setTimeout(() => {
reject()
console.log(`rej #${id} called after ${n} milliseconds`, Date.now() - start)
}, n)
})
}
try {
const delay1 = res(3000)
const delay2 = res(2000)
const delay3 = rej(1000)
const data1 = await delay1
const data2 = await delay2
const data3 = await delay3
} catch (error) {
console.log(`await finished`, Date.now() - start)
}
}
example()

First difference - Fail Fast
I agree with #zzzzBov's answer, but the "fail fast" advantage of Promise.all is not the only difference. Some users in the comments have asked why using Promise.all is worth it when it's only faster in the negative scenario (when some task fails). And I ask, why not? If I have two independent async parallel tasks and the first one takes a very long time to resolve but the second is rejected in a very short time, why leave the user to wait for the longer call to finish to receive an error message? In real-life applications we must consider the negative scenario. But OK - in this first difference you can decide which alternative to use: Promise.all vs. multiple await.
Second difference - Error Handling
But when considering error handling, YOU MUST use Promise.all. It is not possible to correctly handle errors of async parallel tasks triggered with multiple awaits. In the negative scenario, you will always end with UnhandledPromiseRejectionWarning and PromiseRejectionHandledWarning, regardless of where you use try/ catch. That is why Promise.all was designed. Of course someone could say that we can suppress those errors using process.on('unhandledRejection', err => {}) and process.on('rejectionHandled', err => {}) but this is not good practice. I've found many examples on the internet that do not consider error handling for two or more independent async parallel tasks at all, or consider it but in the wrong way - just using try/ catch and hoping it will catch errors. It's almost impossible to find good practice in this.
Summary
TL;DR: Never use multiple await for two or more independent async parallel tasks, because you will not be able to handle errors correctly. Always use Promise.all() for this use case.
Async/ await is not a replacement for Promises, it's just a pretty way to use promises. Async code is written in "sync style" and we can avoid multiple thens in promises.
Some people say that when using Promise.all() we can't handle task errors separately, and that we can only handle the error from the first rejected promise (separate handling can be useful e.g. for logging). This is not a problem - see "Addition" heading at the bottom of this answer.
Examples
Consider this async task...
const task = function(taskNum, seconds, negativeScenario) {
return new Promise((resolve, reject) => {
setTimeout(_ => {
if (negativeScenario)
reject(new Error('Task ' + taskNum + ' failed!'));
else
resolve('Task ' + taskNum + ' succeed!');
}, seconds * 1000)
});
};
When you run tasks in the positive scenario there is no difference between Promise.all and multiple awaits. Both examples end with Task 1 succeed! Task 2 succeed! after 5 seconds.
// Promise.all alternative
const run = async function() {
// tasks run immediately in parallel and wait for both results
let [r1, r2] = await Promise.all([
task(1, 5, false),
task(2, 5, false)
]);
console.log(r1 + ' ' + r2);
};
run();
// at 5th sec: Task 1 succeed! Task 2 succeed!
// multiple await alternative
const run = async function() {
// tasks run immediately in parallel
let t1 = task(1, 5, false);
let t2 = task(2, 5, false);
// wait for both results
let r1 = await t1;
let r2 = await t2;
console.log(r1 + ' ' + r2);
};
run();
// at 5th sec: Task 1 succeed! Task 2 succeed!
However, when the first task takes 10 seconds and succeeds, and the second task takes 5 seconds but fails, there are differences in the errors issued.
// Promise.all alternative
const run = async function() {
let [r1, r2] = await Promise.all([
task(1, 10, false),
task(2, 5, true)
]);
console.log(r1 + ' ' + r2);
};
run();
// at 5th sec: UnhandledPromiseRejectionWarning: Error: Task 2 failed!
// multiple await alternative
const run = async function() {
let t1 = task(1, 10, false);
let t2 = task(2, 5, true);
let r1 = await t1;
let r2 = await t2;
console.log(r1 + ' ' + r2);
};
run();
// at 5th sec: UnhandledPromiseRejectionWarning: Error: Task 2 failed!
// at 10th sec: PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)
// at 10th sec: UnhandledPromiseRejectionWarning: Error: Task 2 failed!
We should already notice here that we are doing something wrong when using multiple awaits in parallel. Let's try handling the errors:
// Promise.all alternative
const run = async function() {
let [r1, r2] = await Promise.all([
task(1, 10, false),
task(2, 5, true)
]);
console.log(r1 + ' ' + r2);
};
run().catch(err => { console.log('Caught error', err); });
// at 5th sec: Caught error Error: Task 2 failed!
As you can see, to successfully handle errors, we need to add just one catch to the run function and add code with catch logic into the callback. We do not need to handle errors inside the run function because async functions do this automatically - promise rejection of the task function causes rejection of the run function.
To avoid a callback we can use "sync style" (async/ await + try/ catch)
try { await run(); } catch(err) { }
but in this example it's not possible, because we can't use await in the main thread - it can only be used in async functions (because nobody wants to block main thread). To test if handling works in "sync style" we can call the run function from another async function or use an IIFE (Immediately Invoked Function Expression: MDN):
(async function() {
try {
await run();
} catch(err) {
console.log('Caught error', err);
}
})();
This is the only correct way to run two or more async parallel tasks and handle errors. You should avoid the examples below.
Bad Examples
// multiple await alternative
const run = async function() {
let t1 = task(1, 10, false);
let t2 = task(2, 5, true);
let r1 = await t1;
let r2 = await t2;
console.log(r1 + ' ' + r2);
};
We can try to handle errors in the code above in several ways...
try { run(); } catch(err) { console.log('Caught error', err); };
// at 5th sec: UnhandledPromiseRejectionWarning: Error: Task 2 failed!
// at 10th sec: UnhandledPromiseRejectionWarning: Error: Task 2 failed!
// at 10th sec: PromiseRejectionHandledWarning: Promise rejection was handled
... nothing got caught because it handles sync code but run is async.
run().catch(err => { console.log('Caught error', err); });
// at 5th sec: UnhandledPromiseRejectionWarning: Error: Task 2 failed!
// at 10th sec: Caught error Error: Task 2 failed!
// at 10th sec: PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)
... huh? We see firstly that the error for task 2 was not handled and later that it was caught. Misleading and still full of errors in console, it's still unusable this way.
(async function() { try { await run(); } catch(err) { console.log('Caught error', err); }; })();
// at 5th sec: UnhandledPromiseRejectionWarning: Error: Task 2 failed!
// at 10th sec: Caught error Error: Task 2 failed!
// at 10th sec: PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)
... the same as above. User #Qwerty in his deleted answer asked about this strange behaviour where an error seems to be caught but are also unhandled. We catch error the because run() is rejected on the line with the await keyword and can be caught using try/ catch when calling run(). We also get an unhandled error because we are calling an async task function synchronously (without the await keyword), and this task runs and fails outside the run() function.
It is similar to when we are not able to handle errors by try/ catch when calling some sync function which calls setTimeout:
function test() {
setTimeout(function() {
console.log(causesError);
}, 0);
};
try {
test();
} catch(e) {
/* this will never catch error */
}`.
Another poor example:
const run = async function() {
try {
let t1 = task(1, 10, false);
let t2 = task(2, 5, true);
let r1 = await t1;
let r2 = await t2;
}
catch (err) {
return new Error(err);
}
console.log(r1 + ' ' + r2);
};
run().catch(err => { console.log('Caught error', err); });
// at 5th sec: UnhandledPromiseRejectionWarning: Error: Task 2 failed!
// at 10th sec: PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)
... "only" two errors (3rd one is missing) but nothing is caught.
Addition (handling separate task errors and also first-fail error)
const run = async function() {
let [r1, r2] = await Promise.all([
task(1, 10, true).catch(err => { console.log('Task 1 failed!'); throw err; }),
task(2, 5, true).catch(err => { console.log('Task 2 failed!'); throw err; })
]);
console.log(r1 + ' ' + r2);
};
run().catch(err => { console.log('Run failed (does not matter which task)!'); });
// at 5th sec: Task 2 failed!
// at 5th sec: Run failed (does not matter which task)!
// at 10th sec: Task 1 failed!
... note that in this example I rejected both tasks to better demonstrate what happens (throw err is used to fire final error).

Generally, using Promise.all() runs requests "async" in parallel. Using await can run in parallel OR be "sync" blocking.
test1 and test2 functions below show how await can run async or sync.
test3 shows Promise.all() that is async.
jsfiddle with timed results - open browser console to see test results
Sync behavior. Does NOT run in parallel, takes ~1800ms:
const test1 = async () => {
const delay1 = await Promise.delay(600); //runs 1st
const delay2 = await Promise.delay(600); //waits 600 for delay1 to run
const delay3 = await Promise.delay(600); //waits 600 more for delay2 to run
};
Async behavior. Runs in paralel, takes ~600ms:
const test2 = async () => {
const delay1 = Promise.delay(600);
const delay2 = Promise.delay(600);
const delay3 = Promise.delay(600);
const data1 = await delay1;
const data2 = await delay2;
const data3 = await delay3; //runs all delays simultaneously
}
Async behavior. Runs in parallel, takes ~600ms:
const test3 = async () => {
await Promise.all([
Promise.delay(600),
Promise.delay(600),
Promise.delay(600)]); //runs all delays simultaneously
};
TLDR; If you are using Promise.all() it will also "fast-fail" - stop running at the time of the first failure of any of the included functions.

You can check for yourself.
In this fiddle, I ran a test to demonstrate the blocking nature of await, as opposed to Promise.all which will start all of the promises and while one is waiting it will go on with the others.

In case of await Promise.all([task1(), task2()]); "task1()" and "task2()" will run parallel and will wait until both promises are completed (either resolved or rejected). Whereas in case of
const result1 = await t1;
const result2 = await t2;
t2 will only run after t1 has finished execution (has been resolved or rejected). Both t1 and t2 will not run parallel.

Just in case, in addition to the already awesome answers:
const rejectAt = 3;
// No worries. "3" is purely awesome, too! Just for the tiny example!
document.body.innerHTML = '';
o("// With 'Promise.all()':");
let a = Promise.all([
test(1),
test(2),
test(3),
test(4),
test(5),
]).then(v => {
o(`+ Look! We got all: ${v}`);
}).catch(e => {
o(`x Oh! Got rejected with '${e}'`);
}).finally(() => {
o("\n// With 'await':");
async function test2() {
try {
r = [];
r.push(await test(1));
r.push(await test(2));
r.push(await test(3));
r.push(await test(4));
r.push(await test(5));
o(`+ Look! We got all: ${r.join(',')} // Twice as happy! ^^`);
} catch (e) {
o(`x Ah! Got rejected with '${e}'`);
}
}
test2();
});
function test(v) {
if (v === rejectAt) {
o(`- Test ${v} (reject)`);
return new Promise((undefined, reject) => reject(v));
}
o(`- Test ${v} (resolve)`);
return new Promise((resolve, undefined) => resolve(v));
}
// ----------------------------------------
// Output
function o(value) {
document.write(`${value}\n`);
}
body {
white-space: pre;
font-family: 'monospace';
}
A possible result:
// With 'Promise.all()':
- Test 1 (resolve)
- Test 2 (resolve)
- Test 3 (reject)
- Test 4 (resolve)
- Test 5 (resolve)
x Oh! Got rejected with '3'
// With 'await':
- Test 1 (resolve)
- Test 2 (resolve)
- Test 3 (reject)
x Ah! Got rejected with '3'

Related

getting same response from a promise multiple times

I have a function that would return a promise, and in the case of an error, I have to call the same function again. The problem is that whenever I call it again, I get the same response, as if it was never called again.
This is how am resolving:
first_file = async () => {
return new Promise(async (resolve, reject) => {
//Generating the token
(async () => {
while (true) {
console.log("Resolving...");
resolve(token);
await sleep(5000);
resolved_token = token;
}
})();
});
};
I'm generating a token here, which I use in the second script:
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
(async() =>{
while(true){
test = require("./test")
test.first_file ().then(res=>{
console.log(res)
})
await sleep(15000)
}
})()
The expected value here is that every 15000ms (15 sec) I get a new response, but here I'm getting the same response over and over again.
Sorry if the title is inaccurate; I didn't know how to explain the problem.
Promises represent a value + time, a promise's settled value doesn't change like the number 5 doesn't change. Calling resolve multiple times is a no-op*.
What you want to do instead of using the language's abstraction for value + time is to use the language's abstraction for action + time - an async function (or just a function returning a promise)
const tokenFactory = () => {
let current = null;
(async () =>
while (true) {
console.log("Resolving...");
current = token; // get token somewhere
await sleep(5000);
}
})().catch((e) => {/* handle error */});
return () => current; // we return a function so it's captured
};
Which will let you do:
tokenFactory(); // first token (or null)
// 5 seconds later
tokenFactory(); // second token
*We have a flag we added in Node.js called multipleResolves that will let you observe that for logging/error handling

How to yield value multiple times from function?

So what I am doing is, I have 2 files, One that contain a script which would generate a token and the second file handle that token.
The problem is that the second script which would log the token it would only log the first token received.
This is the how I am handling the token:
const first_file = require("./first_file.js");
first_file.first_file().then((res) => {
console.log(res);
});
And clearly that wouldn't work, Because it's not getting updated with the newer value.
first_file = async () => {
return new Promise(async (resolve, reject) => {
//Generating the token
(async () => {
while (true) {
console.log("Resolving...");
resolve(token);
await sleep(5000);
resolved_token = token;
}
})();
});
};
module.exports = { first_file };
What I am doing here is, I tried to do a while..loop so that I keep resolving the token. But it didn't, Is there and way I can export the variable directly so the task would be easier ?
If I understand your question correctly, You want to resolve promise multiple times, And It's nothing to do with modules...
But You understand something wrong about promise in JavaScript...
You can't resolve a promise twice.
Generator
But you can generate new value from function, this type of function also known as generator, Where a function can reenter its context (Something like async/await) and yield result using yield keyword.
Usually a generator is used in for..of loop. It has next() method for yield next value from a generator...
Lets look an example:
const delay = ms => new Promise(res => setTimeout(res.bind(null, ms), ms));
async function* generator() {
yield 'yield result from generator!'
for (let ms = 100; ms <= 300; ms += 100) {
yield 'delay: ' + await delay(ms) + ' ms';
}
yield delay(1000).then(() => 'you can also yield promise!');
}
async function main() {
const gen = generator();
console.log('1st', (await gen.next()).value);
for await (const ms of gen) {
console.log(ms)
}
}
main()
Note that * after function, So that we know that this function a generator, with async keyword this is Async Generator.
Generator is very useful. like: Generate value on demand, Pass data like pipe!, Can return endless value from function etc...
Callback
This old school method heavily used in node, Where you pass a callback function as argument.
Example:
const delay = ms => new Promise(res => setTimeout(res.bind(null, ms), ms));
async function callback(fn) {
fn('yield result from callback!');
for (let ms = 100; ms <= 300; ms += 100) {
fn('delay: ' + await delay(ms) + ' ms');
}
await delay(1000);
fn('yield asynchronously!');
}
callback(value => console.log(value));
This approach create all sort of nutsy problem, like: created function scope, disaster of control flow, doesn't have break keyword etc...
I don't recommend this method.

Time delay in Truffle Tests

I am writing test cases for my contract and have to delay the assertion check because it is time sensitive. getCompletedCampaigns() will have the address of the Campaign whose deadline has passed.
it("Time sensitive check", async () => {
var deadLine = Math.round(Date.now() / 1000) + 3;
let eventDetails = await contract.createCampaign("Campaign name",deadLine,
{
from: accounts[0]
});
addressFromEvent = eventDetails['logs'][1]['args']['campaignAddress'];
async function checker() {
let deployedCampaigns = await factory.getCompletedCampaigns();
assert.equal(addressFromEvent, deployedCampaigns[0]);
}
function timeout(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function sleep() {
await timeout(5000);
return checker();
}
sleep();
});
The test passes even if the assertion is supposed to fail. The assertion happens after the test suite has finished executing all tests and forces the prompt to come out of truffle develop console because if it had failed. In the below testing I ve failed the test on purpose.
Contract: Testing CrowdCoin
✓ CampaignFactory deployment
✓ Create a new Campaign (168ms)
✓ Get ongoing Campaigns (246ms)
✓ Get completed Campaigns (189ms)
4 passing (1s)
truffle(develop)>
/home/vagrant/code/test/crowdcoin.test.js:82
assert.equal(0, deployedCampaigns[1]);
^
AssertionError: expected 0 to equal '0x806ea81c279b6000b9fd9f14d2845dec87fc3544'
at checker (/home/vagrant/code/test/crowdcoin.test.js:82:11)
at process._tickCallback (internal/process/next_tick.js:68:7)
How do I make sure the test check happens along with time delay?
it("Time sensitive check", async () => {
var deadLine = Math.round(Date.now() / 1000) + 3;
let eventDetails = await contract.createCampaign("Campaign name",deadLine,
{
from: accounts[0]
});
function timeout(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
await timeout(5000);
addressFromEvent = eventDetails['logs'][1]['args']['campaignAddress'];
let deployedCampaigns = await factory.getCompletedCampaigns();
await assert.equal(addressFromEvent, deployedCampaigns[0]);
});
By moving the entire delay logic to a different method and all logical statements preceded by await, the execution of the said logical statements and test completion is not done unless timeout() has completed.
Actually you can try using time in #openzeppelin/test-helper to speed up your test case as you don't necessary wait for 3 secs running on this test case.
const { time } = require("#openzeppelin/test-helpers");
it("Time sensitive check", async () => {
let duration = time.duration.seconds(3);
let eventDetails = await contract.createCampaign("Campaign name",duration,
{
from: accounts[0]
});
await time.increase(duration);
addressFromEvent = eventDetails['logs'][1]['args']['campaignAddress'];
let deployedCampaigns = await factory.getCompletedCampaigns();
await assert.equal(addressFromEvent, deployedCampaigns[0]);
});
which make more sense on testing timestamp in blockchain.
Doc: https://docs.openzeppelin.com/test-helpers/0.5/api#time
An example of how #openzeppelin team work on test case that you can look into:
https://github.com/OpenZeppelin/openzeppelin-contracts/blob/24a0bc23cfe3fbc76f8f2510b78af1e948ae6651/test/token/ERC20/utils/TokenTimelock.test.js

If I await 2 functions can I guarantee that the return object will have both values

I have the following code
module.exports = async function (req, res) {
const station1 = await getStation('one')
const station2 = await getStation('two')
return { stations: [station1, station2] }
}
Can I be guaranteed that when the final return value is sent it will definitely have both station1 and station2 data in them, or do I need to wrap the function call in a Promise.all()
As you have it, it is guaranteed that the return statement will only be executed when the two getStation() promises have resolved.
However, the second call to getStation will only happen when the first promise has resolved, making them run in serial. As there is no dependency between them, you could gain performance, if you would run them in parallel.
Although this can be achieved with Promise.all, you can achieve the same by first retrieving the two promises and only then performing the await on them:
module.exports = async function (req, res) {
const promise1 = getStation('one');
const promise2 = getStation('two');
return { stations: [await promise1, await promise2] }
}
Now both calls will be performed at the "same" time, and it will be just the return statement that will be pending for both promises to resolve. This is also illustrated in MDN's "simple example".
The await keyword actually makes you "wait" on the line of code, while running an async action.
That means that you don't proceed to the next line of code until the async action is resolved. This is good if your code has a dependency with the result.
Example:
const res1 = await doSomething();
if(res1.isValid)
{
console.log('do something with res1 result');
}
The following code example will await a promise that gets resolved after three seconds. Check the date prints to the console to understand what await does:
async function f1() {
console.log(new Date());
// Resolve after three seconds
var p = new Promise(resolve => {
setTimeout(() => resolve({}),3000);
});
await p;
console.log(new Date());
}
f1();
ES6Console
BTW, In your case, since you don't use the result of station1 it's better using Promise.all to work parallel.
Check this example (it will run for 3 seconds instead of 4 seconds the way you coded above):
async function f1() {
console.log(new Date());
// Resolve after three seconds
var p1 = new Promise(resolve => {
setTimeout(() => resolve({a:1}),3000);
});
// Resolve after one second
var p2 = new Promise(resolve => {
setTimeout(() => resolve({a:2}),1000);
});
// Run them parallel - Max(three seconds, one second) -> three seconds.
var res = await Promise.all([p1,p2]);
console.log(new Date());
console.log('result:' + res);
}
f1();
ES6Console.
If either of await getStation('one') or await getStation('two') fails an exception will be thrown from the async function. So you should always get the resolved value from both promises.
You can rewrite your function as follows to use Promise.all
module.exports = async function (req, res) {
try{
const [station1, station2] = await Promise.all([
getStation('one'),
getStation('two')
]);
return { stations: [station1, station2] };
} catch (e) {
throw e;
}
}

How do I access <state> and <value> properties of a Promise? [duplicate]

I have a pure JavaScript Promise (built-in implementation or poly-fill):
var promise = new Promise(function (resolve, reject) { /* ... */ });
From the specification, a Promise can be one of:
'settled' and 'resolved'
'settled' and 'rejected'
'pending'
I have a use case where I wish to interrogate the Promise synchronously and determine:
is the Promise settled?
if so, is the Promise resolved?
I know that I can use #then() to schedule work to be performed asynchronously after the Promise changes state. I am NOT asking how to do this.
This question is specifically about synchronous interrogation of a Promise's state. How can I achieve this?
No such synchronous inspection API exists for native JavaScript promises. It is impossible to do this with native promises. The specification does not specify such a method.
Userland libraries can do this, and if you're targeting a specific engine (like v8) and have access to platform code (that is, you can write code in core) then you can use specific tools (like private symbols) to achieve this. That's super specific though and not in userland.
Nope, no sync API, but here's my version of the async promiseState (with help from #Matthijs):
function promiseState(p) {
const t = {};
return Promise.race([p, t])
.then(v => (v === t)? "pending" : "fulfilled", () => "rejected");
}
var a = Promise.resolve();
var b = Promise.reject();
var c = new Promise(() => {});
promiseState(a).then(state => console.log(state)); // fulfilled
promiseState(b).then(state => console.log(state)); // rejected
promiseState(c).then(state => console.log(state)); // pending
promise-status-async does the trick. It is async but it does not use then to wait the promise to be resolved.
const {promiseStatus} = require('promise-status-async');
// ...
if (await promiseStatus(promise) === 'pending') {
const idle = new Promise(function(resolve) {
// can do some IDLE job meanwhile
});
return idle;
}
You can make a race with Promise.resolve
It's not synchronous but happens now
function promiseState(p, isPending, isResolved, isRejected) {
Promise.race([p, Promise.resolve('a value that p should not return')]).then(function(value) {
if (value == 'a value that p should not return') {
(typeof(isPending) === 'function') && isPending();
}else {
(typeof(isResolved) === 'function') && isResolved(value);
}
}, function(reason) {
(typeof(isRejected) === 'function') && isRejected(reason);
});
}
A little script for testing and understand their meaning of asynchronously
var startTime = Date.now() - 100000;//padding trick "100001".slice(1) => 00001
function log(msg) {
console.log((""+(Date.now() - startTime)).slice(1) + ' ' + msg);
return msg;//for chaining promises
};
function prefix(pref) { return function (value) { log(pref + value); return value; };}
function delay(ms) {
return function (value) {
var startTime = Date.now();
while(Date.now() - startTime < ms) {}
return value;//for chaining promises
};
}
setTimeout(log, 0,'timeOut 0 ms');
setTimeout(log, 100,'timeOut 100 ms');
setTimeout(log, 200,'timeOut 200 ms');
var p1 = Promise.resolve('One');
var p2 = new Promise(function(resolve, reject) { setTimeout(resolve, 100, "Two"); });
var p3 = Promise.reject("Three");
p3.catch(delay(200)).then(delay(100)).then(prefix('delayed L3 : '));
promiseState(p1, prefix('p1 Is Pending '), prefix('p1 Is Resolved '), prefix('p1 Is Rejected '));
promiseState(p2, prefix('p2 Is Pending '), prefix('p2 Is Resolved '), prefix('p2 Is Rejected '));
promiseState(p3, prefix('p3 Is Pending '), prefix('p3 Is Resolved '), prefix('p3 Is Rejected '));
p1.then(prefix('Level 1 : ')).then(prefix('Level 2 : ')).then(prefix('Level 3 : '));
p2.then(prefix('Level 1 : ')).then(prefix('Level 2 : ')).then(prefix('Level 3 : '));
p3.catch(prefix('Level 1 : ')).then(prefix('Level 2 : ')).then(prefix('Level 3 : '));
log('end of promises');
delay(100)();
log('end of script');
results with delay(0) (comment the while in delay)
00001 end of promises
00001 end of script
00001 Level 1 : One
00001 Level 1 : Three
00001 p1 Is Resolved One
00001 p2 Is Pending undefined
00001 p3 Is Rejected Three
00001 Level 2 : One
00001 Level 2 : Three
00001 delayed L3 : Three
00002 Level 3 : One
00002 Level 3 : Three
00006 timeOut 0 ms
00100 timeOut 100 ms
00100 Level 1 : Two
00100 Level 2 : Two
00101 Level 3 : Two
00189 timeOut 200 ms
and the results of this test with firefox(chrome keep the order)
00000 end of promises
00100 end of script
00300 Level 1 : One
00300 Level 1 : Three
00400 p1 Is Resolved One
00400 p2 Is Pending undefined
00400 p3 Is Rejected Three
00400 Level 2 : One
00400 Level 2 : Three
00400 delayed L3 : Three
00400 Level 3 : One
00400 Level 3 : Three
00406 timeOut 0 ms
00406 timeOut 100 ms
00406 timeOut 200 ms
00406 Level 1 : Two
00407 Level 2 : Two
00407 Level 3 : Two
promiseState make .race and .then : Level 2
in node, say undocumented internal process.binding('util').getPromiseDetails(promise)
> process.binding('util').getPromiseDetails(Promise.resolve({data: [1,2,3]}));
[ 1, { data: [ 1, 2, 3 ] } ]
> process.binding('util').getPromiseDetails(Promise.reject(new Error('no')));
[ 2, Error: no ]
> process.binding('util').getPromiseDetails(new Promise((resolve) => {}));
[ 0, <1 empty item> ]
You can use an (ugly) hack in Node.js until a native method is offered:
util = require('util');
var promise1 = new Promise (function (resolve) {
}
var promise2 = new Promise (function (resolve) {
resolve ('foo');
}
state1 = util.inspect (promise1);
state2 = util.inspect (promise2);
if (state1 === 'Promise { <pending> }') {
console.log('pending'); // pending
}
if (state2 === "Promise { 'foo' }") {
console.log ('foo') // foo
}
Updated: 2019
Bluebird.js offers this: http://bluebirdjs.com/docs/api/isfulfilled.html
var Promise = require("bluebird");
let p = Promise.resolve();
console.log(p.isFulfilled());
If you'd prefer to create your own wrapper, here is a nice blog about it.
Because JavaScript is single-threaded, it's hard to find a common enough use case to justify putting this in the spec. The best place to know if a promise is resolved is in .then(). Testing if a Promise is fullfilled would create a polling loop which is most likely the wrong direction.
async/await is a nice construct if you'd like to reason async code synchronously.
await this();
await that();
return 'success!';
Another useful call is Promise.all()
var promise1 = Promise.resolve(3);
var promise2 = 42;
var promise3 = new Promise(function(resolve, reject) {
setTimeout(resolve, 100, 'foo');
});
Promise.all([promise1, promise2, promise3]).then(function(values) {
console.log(values);
});
// expected output: Array [3, 42, "foo"]
When I first reached for this answer, that is the use case I was looking for.
It's indeed quite annoying that this basic functionality is missing. If you're using node.js then I know of two workarounds, neither of 'em very pretty. Both snippets below implement the same API:
> Promise.getInfo( 42 ) // not a promise
{ status: 'fulfilled', value: 42 }
> Promise.getInfo( Promise.resolve(42) ) // fulfilled
{ status: 'fulfilled', value: 42 }
> Promise.getInfo( Promise.reject(42) ) // rejected
{ status: 'rejected', value: 42 }
> Promise.getInfo( p = new Promise(() => {}) ) // unresolved
{ status: 'pending' }
> Promise.getInfo( Promise.resolve(p) ) // resolved but pending
{ status: 'pending' }
There doesn't seem to be any way to distinguish the last two promise states using either trick.
1. Use the V8 debug API
This is the same trick that util.inspect uses.
const Debug = require('vm').runInDebugContext('Debug');
Promise.getInfo = function( arg ) {
let mirror = Debug.MakeMirror( arg, true );
if( ! mirror.isPromise() )
return { status: 'fulfilled', value: arg };
let status = mirror.status();
if( status === 'pending' )
return { status };
if( status === 'resolved' ) // fix terminology fuck-up
status = 'fulfilled';
let value = mirror.promiseValue().value();
return { status, value };
};
2. Synchronously run microtasks
This avoids the debug API, but has some frightening semantics by causing all pending microtasks and process.nextTick callbacks to be run synchronously. It also has the side-effect of preventing the "unhandled promise rejection" error from ever being triggered for the inspected promise.
Promise.getInfo = function( arg ) {
const pending = {};
let status, value;
Promise.race([ arg, pending ]).then(
x => { status = 'fulfilled'; value = x; },
x => { status = 'rejected'; value = x; }
);
process._tickCallback(); // run microtasks right now
if( value === pending )
return { status: 'pending' };
return { status, value };
};
await usage to #jib's answer, with idiomatic prototyping.
Object.defineProperty(Promise.prototype, "state", {
get: function(){
const o = {};
return Promise.race([this, o]).then(
v => v === o ? "pending" : "resolved",
() => "rejected");
}
});
// usage: console.log(await <Your Promise>.state);
(async () => {
console.log(await Promise.resolve(2).state); // "resolved"
console.log(await Promise.reject(0).state); // "rejected"
console.log(await new Promise(()=>{}).state); // "pending"
})();
note that this async function execute "almost" immediately like synced function (or actually possibly be instantly).
You can wrap your promises in this way
function wrapPromise(promise) {
var value, error,
settled = false,
resolved = false,
rejected = false,
p = promise.then(function(v) {
value = v;
settled = true;
resolved = true;
return v;
}, function(err) {
error = err;
settled = true;
rejected = true;
throw err;
});
p.isSettled = function() {
return settled;
};
p.isResolved = function() {
return resolved;
};
p.isRejected = function() {
return rejected;
};
p.value = function() {
return value;
};
p.error = function() {
return error;
};
var pThen = p.then, pCatch = p.catch;
p.then = function(res, rej) {
return wrapPromise(pThen(res, rej));
};
p.catch = function(rej) {
return wrapPromise(pCatch(rej));
};
return p;
}
I looked through the solutions proposed to this question and could not see one that corresponds to a simple approach that I have used in Node.js.
I have defined a simple class PromiseMonitor, which takes a promise as the single parameter to its constructor, and has a string property .status which returns the standard string values corresponding to the promise status, "pending", "resolved" or "rejected", and four boolean properties .pending, .resolved, .rejected and .error. The property .error is set true only if .rejected is true and the reject callback was passed an Error object.
The class simply uses .then() on the promise to change the status of the PromiseMonitor when the promise is resolved or rejected. It does not interfere with any other use of the original promise. Here is the code:
class PromiseMonitor {
constructor(prm){
this._status = "pending";
this._pending = true;
this._resolved = false;
this._rejected = false;
this._error = false;
prm
.then( ()=>{
this._status = "resolved";
this._resolved = true;
this._pending = false;
}
, (err)=>{
this._status = "rejected";
this._pending = false;
this._rejected = true;
this._error = err instanceof Error ? true: false ;
}
);
}
get status(){ return this._status; };
get pending(){ return this._pending; };
get resolved(){ return this._resolved; };
get rejected(){ return this._rejected; };
get error(){ return this._error };
};
To monitor the status of a Promise, simply create an instance of PromiseMonitor, passing the promise in as a parameter, for example:
let promiseObject = functionThatReturnsAPromise();
let promiseMonitor = new PromiseMonitor( promiseObject );
Now you can syncrhonously check all the properties of promiseMonitor, which will track the status of the original promise. Here is a test script that demonstrates the three possible resolutions of a promise being monitored.
let ticks = 0;
let tickerID = setInterval( ()=>{++ticks; console.log(`..tick ${ticks}`)}, 1000);
async function run(){
console.log("Start");
let delay = prmDelay(2000);
let delayMonitor = new PromiseMonitor(delay);
// normal handling of delay promise
delay.then((result)=>( console.log("Normal resolution of delay using .then()") ) );
console.log("delay at start:\n", delay);
console.log("delayMonitor at start:\n", delayMonitor);
await delay;
console.log("delay finished:\n", delay);
console.log("delayMonitor finished:\n", delayMonitor);
console.log("\n\n TEST2: Rejection without an Error test ================================")
let rejDelay = prmDelay(3000, "reject");
let rejMonitor = new PromiseMonitor(rejDelay);
// normal handling of reject result on promise
rejDelay.then((result)=>( console.log("Normal resolution of rejDelay using .then will not happen") )
, (err)=>( console.log("Rejection of rejDelay handled using .then")));
console.log("rejDelay at start:\n", rejDelay);
console.log("rejMonitor at start:\n", rejMonitor);
await rejDelay.catch( (err)=>{ console.log( "Caught error using .catch on rejDelay" ); });
console.log("rejDelay finished:\n", rejDelay);
console.log("rejMonitor finished:\n", rejMonitor);
console.log("\n\n TEST3: Rejection with an Error test ================================")
let errMonitor ;
let errDelay;
try{
errDelay = prmDelay(1000, "error");
errMonitor = new PromiseMonitor(errDelay);
// normal handling of results of the original promise
errDelay.then(
(result)=>{
console.log("Normal expiry of errDelay");
console.log("Monitor Status is " + errMonitor.status )
}
, (err)=>{
console.log("** Rejection of errDelay handled using .then()");
console.log(" Monitor Status is " + errMonitor.status )
}
);
console.log("errDelay at start:\n", errDelay);
console.log("errMonitor at start:\n", errMonitor);
await errDelay;
console.log("**** This should never be run");
} catch(err) {
console.log( "** Caught error on errDelay using try{}catch{}:" );
console.log( " Monitor Status is " + errMonitor.status )
};
console.log("errDelay finished:\n", errDelay);
console.log("errMonitor finished:\n", errMonitor);
clearInterval(tickerID);
}
/**
* Creates a new promise with a specific result
* #param {*} tt
* #param {*} exitType ("resolve", "reject" or "error")
*/
function prmDelay (tt, exitType) {
return new Promise(function(resolve, reject) {
if( exitType == 'reject' ){
setTimeout(()=>{ reject("REJECTED")}, tt);
} else if( exitType== 'error'){
setTimeout(()=>{ reject(new Error( "ERROR Rejection") ); }, tt);
} else {
setTimeout(()=>{ resolve("RESOLVED") }, tt);
} ;
});
};
run();
You can add a method to Promise.prototype. It looks like this:
Edited: The first solution is not working properly, like most of the answers here. It returns "pending" until the asynchronous function ".then" is invoked, which is not happen immediately. (The same is about solutions using Promise.race). My second solution solves this problem.
if (window.Promise) {
Promise.prototype.getState = function () {
if (!this.state) {
this.state = "pending";
var that = this;
this.then(
function (v) {
that.state = "resolved";
return v;
},
function (e) {
that.state = "rejected";
return e;
});
}
return this.state;
};
}
You can use it on any Promise. For exemple:
myPromise = new Promise(myFunction);
console.log(myPromise.getState()); // pending|resolved|rejected
Second (and correct) solution:
if (window.Promise) {
Promise.stateable = function (func) {
var state = "pending";
var pending = true;
var newPromise = new Promise(wrapper);
newPromise.state = state;
return newPromise;
function wrapper(resolve, reject) {
func(res, rej);
function res(e) {
resolve(e);
if (pending) {
if (newPromise)
newPromise.state = "resolved";
else
state = "resolved";
pending = false;
}
}
function rej(e) {
reject(e);
if (pending) {
if (newPromise)
newPromise.state = "rejected";
else
state = "rejected";
pending = false;
}
}
}
};
}
And use it:
Notice: In this solution you doesn't have to use the "new" operator.
myPromise = Promise.stateable(myFunction);
console.log(myPromise.state); // pending|resolved|rejected
Caveat: This method uses undocumented Node.js internals and could be changed without warning.
In Node you can synchronously determine a promise's state using process.binding('util').getPromiseDetails(/* promise */);.
This will return:
[0, ] for pending,
[1, /* value */] for fulfilled, or
[2, /* value */] for rejected.
const pending = new Promise(resolve => setTimeout(() => resolve('yakko')));;
const fulfilled = Promise.resolve('wakko');
const rejected = Promise.reject('dot');
[pending, fulfilled, rejected].forEach(promise => {
console.log(process.binding('util').getPromiseDetails(promise));
});
// pending: [0, ]
// fulfilled: [1, 'wakko']
// rejected: [2, 'dot']
Wrapping this into a helper function:
const getStatus = promise => ['pending', 'fulfilled', 'rejected'][
process.binding('util').getPromiseDetails(promise)[0]
];
getStatus(pending); // pending
getStatus(fulfilled); // fulfilled
getStatus(rejected); // rejected
There's another elegant & hacky way of checking if a promise is still pending just by converting the whole object to string and check it with the help of inspect like this: util.inspect(myPromise).includes("pending").
Tested on Node.js 8,9,10,11,12,13
Here's a full example
const util = require("util")
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
(async ()=>{
let letmesleep = sleep(3000)
setInterval(()=>{
console.log(util.inspect(letmesleep).includes("pending"))
},1000)
})()
Result:
true
true
false
false
false
what you can do, is to use a variable to store the state, manually set the state to that variable, and check that variable.
var state = 'pending';
new Promise(function(ff, rjc) {
//do something async
if () {//if success
state = 'resolved';
ff();//
} else {
state = 'rejected';
rjc();
}
});
console.log(state);//check the state somewhere else in the code
of course, this means you must have access to the original code of the promise. If you don't, then you can do:
var state = 'pending';
//you can't access somePromise's code
somePromise.then(function(){
state = 'resolved';
}, function() {
state = 'rejected';
})
console.log(state);//check the promise's state somewhere else in the code
My solution is more coding, but I think you probably wouldn't have to do this for every promise you use.
As of Node.js version 8, you can now use the wise-inspection package to synchronously inspect native promises (without any dangerous hacks).
I made a package for this. Unlike most of the other answers here, it doesn't swallow unhandled rejections.
npm install p-state
import timers from 'timers/promises';
import {promiseStateSync} from 'p-state';
const timeoutPromise = timers.setTimeout(100);
console.log(promiseStateSync(timeoutPromise));
//=> 'pending'
await timeoutPromise;
console.log(promiseStateSync(timeoutPromise));
//=> 'fulfilled'
It looks like somehow nobody came up with one of the simplest solution that doesn't require any hacks:
define a variable to indicate that the promise is running
Add a .finally clause to the promise that sets the variable to false (you can do it at any time after the promise is created)
After that in your code just check if the above variable is true or false, to see whether the Promise is still running.
If you want to know not just whether it's finished or not then instead of .finally add a .then and a .catch clauses that set the variable to "resolved" or "rejected".
The only drawback is that the state variable doesn't get set right away (synchronously) when you add the clauses, in case the promise has already finished. Because of this, it's best to add this to the earliest possible place after the creation of the promise.
Example:
async function worker(){
// wait a very short period of time
await (new Promise(resolve => setTimeout(resolve, 100)))
//...
}
const w1=worker()
let w1_running=true
w1.finally( ()=> {w1_running=false});
//...
//Then check if it's running
(async ()=>{
while(true){
if (w1_running) {
console.log("Still Busy :(")
} else {
console.log("All done :)")
break
}
await (new Promise(resolve => setTimeout(resolve, 10)))
}
})()
// Note we need some async action started otherwise the event loop would never reach the code in the function `worker` or in the `.finally` clause
Here is a more fleshed out es6 version of the QueryablePromise, allowing the ability to chain then and catch after the first resolve and to immediately resolve or reject to keep the api consistent with the native Promise.
const PROMISE = Symbol('PROMISE')
const tap = fn => x => (fn(x), x)
const trace = label => tap(x => console.log(label, x))
class QueryablePromise {
resolved = false
rejected = false
fulfilled = false
catchFns = []
constructor(fn) {
this[PROMISE] = new Promise(fn)
.then(tap(() => {
this.fulfilled = true
this.resolved = true
}))
.catch(x => {
this.fulfilled = true
this.rejected = true
return Promise.reject(x)
})
}
then(fn) {
this[PROMISE].then(fn)
return this
}
catch(fn) {
this[PROMISE].catch(fn)
return this
}
static resolve(x) {
return new QueryablePromise((res) => res(x))
}
static reject(x) {
return new QueryablePromise((_, rej) => rej(x))
}
}
const resolvedPromise = new QueryablePromise((res) => {
setTimeout(res, 200, 'resolvedPromise')
})
const rejectedPromise = new QueryablePromise((_, rej) => {
setTimeout(rej, 200, 'rejectedPromise')
})
// ensure our promises have not been fulfilled
console.log('test 1 before: is resolved', resolvedPromise.resolved)
console.log('test 2 before: is rejected', rejectedPromise.rejected)
setTimeout(() => {
// check to see the resolved status of our promise
console.log('test 1 after: is resolved', resolvedPromise.resolved)
console.log('test 2 after: is rejected', rejectedPromise.rejected)
}, 300)
// make sure we can immediately resolve a QueryablePromise
const immediatelyResolvedPromise = QueryablePromise.resolve('immediatelyResolvedPromise')
// ensure we can chain then
.then(trace('test 3 resolved'))
.then(trace('test 3 resolved 2'))
.catch(trace('test 3 rejected'))
// make sure we can immediately reject a QueryablePromise
const immediatelyRejectedPromise = QueryablePromise.reject('immediatelyRejectedPromise')
.then(trace('test 4 resolved'))
.catch(trace('test 4 rejected'))
<script src="https://codepen.io/synthet1c/pen/KyQQmL.js"></script>
2019:
The simple way to do that as I know is thenable , super thin wrapper around promise or any async job.
const sleep = (t) => new Promise(res => setTimeout(res,t));
const sleeping = sleep(30);
function track(promise){
let state = 'pending';
promise = promise.finally( _=> state ='fulfilled');
return {
get state(){return state},
then: promise.then.bind(promise), /*thentable*/
finally:promise.finally.bind(promise),
catch:promise.catch.bind(promise),
}
}
promise = track(sleeping);
console.log(promise.state) // pending
promise.then(function(){
console.log(promise.state); // fulfilled
})
You can extend the Promise class to create a new queryable Promise
class.
You can create your own subclass, say QueryablePromise, by inheriting from the natively available Promise class, the instances of which would have a status property available on it that you can use to query the status of the promise objects synchronously. An implementation of it can be seen below or refer this for a better explanation.
class QueryablePromise extends Promise {
constructor (executor) {
super((resolve, reject) => executor(
data => {
resolve(data)
this._status = 'Resolved'
},
err => {
reject(err)
this._status = 'Rejected'
},
))
this._status = 'Pending'
}
get status () {
return this._status
}
}
// Create a promise that resolves after 5 sec
var myQueryablePromise = new QueryablePromise((resolve, reject) => {
setTimeout(() => resolve(), 5000)
})
// Log the status of the above promise every 500ms
setInterval(() => {
console.log(myQueryablePromise.status)
}, 500)
CAVEAT: process.binding('util').getPromiseDetails is undefined on node 16!
Benchmark:
Candidates:
/**
* https://stackoverflow.com/a/47009572/5318303
*/
const isPromisePending1 = (() => { // noinspection JSUnresolvedFunction
const util = process.binding('util') // noinspection JSUnresolvedFunction
return promise => !util.getPromiseDetails(promise)[0]
})()
/**
* https://stackoverflow.com/a/35852666/5318303
*/
const isPromisePending2 = (promise) => util.inspect(promise) === 'Promise { <pending> }'
/**
* https://stackoverflow.com/a/35820220/5318303
*/
const isPromisePending3 = (promise) => {
const t = {}
return Promise.race([promise, t])
.then(v => v === t, () => false)
}
Test promises:
const a = Promise.resolve()
const b = Promise.reject()
const c = new Promise(() => {})
const x = (async () => 1)()
Run benchmark:
const n = 1000000
console.time('isPromisePending1')
for (let i = 0; i < n; i++) {
isPromisePending1(a)
isPromisePending1(b)
isPromisePending1(c)
isPromisePending1(x)
}
console.timeEnd('isPromisePending1')
console.time('isPromisePending2')
for (let i = 0; i < n; i++) {
isPromisePending2(a)
isPromisePending2(b)
isPromisePending2(c)
isPromisePending2(x)
}
console.timeEnd('isPromisePending2')
console.time('isPromisePending3')
for (let i = 0; i < n; i++) {
await isPromisePending3(a)
await isPromisePending3(b)
await isPromisePending3(c)
await isPromisePending3(x)
}
console.timeEnd('isPromisePending3')
Result:
isPromisePending1: 440.694ms
isPromisePending2: 3.354s
isPromisePending3: 4.761s
Obviously isPromisePending1() is too faster (8~10 times)! But it's not usable on node 16! (see above caveat).
If you're using ES7 experimental you can use async to easily wrap the promise you want to listen.
async function getClient() {
let client, resolved = false;
try {
client = await new Promise((resolve, reject) => {
let client = new Client();
let timer = setTimeout(() => {
reject(new Error(`timeout`, 1000));
client.close();
});
client.on('ready', () => {
if(!resolved) {
clearTimeout(timer);
resolve(client);
}
});
client.on('error', (error) => {
if(!resolved) {
clearTimeout(timer);
reject(error);
}
});
client.on('close', (hadError) => {
if(!resolved && !hadError) {
clearTimeout(timer);
reject(new Error("close"));
}
});
});
resolved = true;
} catch(error) {
resolved = true;
throw error;
}
return client;
}
I've written a little npm package, promise-value, which provides a promise wrapper with a resolved flag:
https://www.npmjs.com/package/promise-value
It also gives synchronous access to the promise value (or error). This doesn't alter the Promise object itself, following the wrap rather than extend pattern.
This is older question but I was trying to do something similar. I need to keep n workers going. They are structured in a promise. I need to scan and see if they are resolved, rejected or still pending. If resolved, I need the value, if rejected do something to correct the issue or pending. If resolved or rejected I need to start another task to keep n going. I can't figure a way to do it with Promise.all or Promise.race as I keep working promises in an array and can find no way to delete them. So I create a worker that does the trick
I need a promise generator function that returns a promise which resolves or rejects as necessary. It is called by a function that sets up the framework to know what the promise is doing.
In the code below the generator simply returns a promise based on setTimeout.
Here it is
//argObj should be of form
// {succeed: <true or false, nTimer: <desired time out>}
function promiseGenerator(argsObj) {
let succeed = argsObj.succeed;
let nTimer = argsObj.nTimer;
return new Promise((resolve, reject) => {
setTimeout(() => {
if (succeed) {
resolve('ok');
}
else {
reject(`fail`);
}
}, nTimer);
})
}
function doWork(generatorargs) {
let sp = { state: `pending`, value: ``, promise: "" };
let p1 = promiseGenerator(generatorargs)
.then((value) => {
sp.state = "resolved";
sp.value = value;
})
.catch((err) => {
sp.state = "rejected";
sp.value = err;
})
sp.promise = p1;
return sp;
}
doWork returns an object containing the promise and the its state and returned value.
The following code runs a loop that tests the state and creates new workers to keep it at 3 running workers.
let promiseArray = [];
promiseArray.push(doWork({ succeed: true, nTimer: 1000 }));
promiseArray.push(doWork({ succeed: true, nTimer: 500 }));
promiseArray.push(doWork({ succeed: false, nTimer: 3000 }));
function loopTimerPromise(delay) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('ok');
}, delay)
})
}
async function looper() {
let nPromises = 3; //just for breaking loop
let nloop = 0; //just for breaking loop
let i;
//let continueLoop = true;
while (true) {
await loopTimerPromise(900); //execute loop every 900ms
nloop++;
//console.log(`promiseArray.length = ${promiseArray.length}`);
for (i = promiseArray.length; i--; i > -1) {
console.log(`index ${i} state: ${promiseArray[i].state}`);
switch (promiseArray[i].state) {
case "pending":
break;
case "resolved":
nPromises++;
promiseArray.splice(i, 1);
promiseArray.push(doWork({ succeed: true, nTimer: 1000 }));
break;
case "rejected":
//take recovery action
nPromises++;
promiseArray.splice(i, 1);
promiseArray.push(doWork({ succeed: false, nTimer: 500 }));
break;
default:
console.log(`error bad state in i=${i} state:${promiseArray[i].state} `)
break;
}
}
console.log(``);
if (nloop > 10 || nPromises > 10) {
//should do a Promise.all on remaining promises to clean them up but not for test
break;
}
}
}
looper();
Tested in node.js
BTW Not in this answer so much but in others on similar topics, I HATE it when someone says "you don't understand" or "that's not how it works" I generally assume the questioner knows what they want. Suggesting a better way is great. A patient explanation of how promises work would also be good.
Old question with many answers but none seem to suggest what I think is the simplest solution: set a bool indicator on promise resolution/rejection.
class Promise2 {
constructor(...args) {
let promise = new Promise(...args);
promise.then(() => promise._resolved_ = true);
promise.catch(() => promise._rejected_ = true);
return promise;
}
}
let p = new Promise2(r => setTimeout(r, 3000));
setInterval(() => {
console.log('checking synchronously if p is resolved yet?', p._resolved_);
}, 1000);
This is the Future pattern I use: (https://github.com/Smallscript-Corp)
enables sync and async fn usage
enables event patterns to be unified with async behavior
class XPromise extends Promise {
state = 'pending'
get settled() {return(this.state !== 'pending')}
resolve(v,...a) {
this.state = 'resolved'
return(this.resolve_(this.value = v,...a))
}
reject(e,...a) {
this.state = 'rejected'
return(this.reject_(this.value = (e instanceof Error) ? e : XPromise.Error(e),...a))
}
static Error(e) {const v = Error('value-rejected'); v.value = e; return(v)}
static Future(fn,...args) { // FactoryFn
let r,t,fv = new XPromise((r_,t_) => {r=r_;t=t_})
fv.resolve_ = r; fv.reject_ = t;
switch(typeof fn) {
case 'undefined': break; case 'function': fn(fv,...args); break;
default: fv.resolve(fn)
}
return(fv)
}
}
global.Future = XPromise.Future
Then you can create future-value instances that can be resolved using sync and async functions; enables handling events uniformly.
You can use it to write a pattern like:
async doSomething() {
// Start both - logically async-parallel
const fvIsNetworkOnLine = this.fvIsNetworkOnline
const fvAuthToken = this.fvAuthToken
// await both (order not critical since both started/queued above)
await fvAuthToken
await fvIsNetworkOnLine
// ... we can check the future values here if needed `fv.resolved`, `fv.state` etc
// ... do dependent workflow here ...
}
onNetworkOnLine(fIsOnline) {
// We utilize the `fv.settled` below, and use the event to `settle` it etc
if(fIsOnline) {
if(this.fvNetworkAvailable_)
this.fvNetworkAvailable_.resolve(true)
this.fvNetworkAvailable_ = undefined
}
else if(this.fvNetworkAvailable_.settled) {
this.fvNetworkAvailable_ = undefined
}
}
get fvNetworkAvailable() {
if(navigator.onLine)
return true
else if(this.fvNetworkAvailable_)
return this.fvNetworkAvailable_
return (this.fvNetworkAvailable_ = Future())
}
get fvAuthToken() {
if(this.fvAuthToken_)
return this.fvAuthToken_
const authTokenFv = async fv => {
// ... handle retry logic etc here ...
}
return(this.fvAuthToken_ = Future(authTokenFv))
}
I found this solution to be simple and allow me to continue using native promises but add useful synchronous checks. I also didn't have to pull in an entire promise library.
CAVEAT: This only works if there is some sort of break in the current execution thread to allow the promises to execute BEFORE checking the synchronous constructs. That makes this of more limited usefulness than I'd initially thought -- still useful for my use case though (Thanks Benjamin Gruenbaum for pointing this out)
/**
* This function allow you to modify a JS Promise by adding some status properties.
* Based on: http://stackoverflow.com/questions/21485545/is-there-a-way-to-tell-if-an-es6-promise-is-fulfilled-rejected-resolved
* But modified according to the specs of promises : https://promisesaplus.com/
*/
function MakeQuerablePromise(promise) {
// Don't modify any promise that has been already modified.
if (promise.isFulfilled) return promise;
// Set initial state
var isPending = true;
var isRejected = false;
var isFulfilled = false;
// Observe the promise, saving the fulfillment in a closure scope.
var result = promise.then(
function(v) {
isFulfilled = true;
isPending = false;
return v;
},
function(e) {
isRejected = true;
isPending = false;
throw e;
}
);
result.isFulfilled = function() { return isFulfilled; };
result.isPending = function() { return isPending; };
result.isRejected = function() { return isRejected; };
return result;
}
wrappedPromise = MakeQueryablePromise(Promise.resolve(3));
setTimeout(function() {console.log(wrappedPromise.isFulfilled())}, 1);
From https://ourcodeworld.com/articles/read/317/how-to-check-if-a-javascript-promise-has-been-fulfilled-rejected-or-resolved which based their answer on Is there a way to tell if an ES6 promise is fulfilled/rejected/resolved?

Categories

Resources