Master de II. ULL. 1er cuatrimestre. 2020/2021
If you use for-await-of on an array of promises, you iterate over it in the specified order, doesn’t matter if the next promise in the given array is resolved before the previous one:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const sleep = time => new Promise(resolve => setTimeout(resolve, time));
(async function () {
    const arr = [
        sleep(2000).then(() => 'a'),
        'x',
        sleep(1000).then(() => 'b'),
        'y',
        sleep(3000).then(() => 'c'),
        'z',
    ];
    for await (const item of arr) {
        console.log(item);
    }
}());
Output:
1
2
3
4
5
6
7
➜  firstcomefirstserved git:(main) node examples/for-await-simple.js 
a
x
b
y
c
z
But sometimes you want to process the results as soon as the promises yield them.
Write a Node.JS module frstcmfrstsvd that exports an async generator that can be used with for-await-of and provides the results in a first come first served order:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import firstComeFirstServed from 'frstcmfrstsvd';
// See https://stackoverflow.com/questions/40920179/should-i-refrain-from-handling-promise-rejection-asynchronously
process.on('rejectionHandled', () => { });
process.on('unhandledRejection', error => {
    console.log('unhandledRejection');
});
const sleep = time => new Promise(resolve => setTimeout(resolve, time));
const arr = [
    sleep(2000).then(() => 'a'),
    'x',
    sleep(1000).then(() => 'b'),
    'y',
    sleep(3000).then(() => 'c'),
    'z',
];
console.log(firstComeFirstServed);
(async () => {
    for await (let item of firstComeFirstServed(arr)) {
        console.log("item = ",item);
    }
})()
Output:
1
2
3
4
5
6
7
8
➜  firstcomefirstserved git:(main) node examples/hello-frstcmfrstsvd.mjs 
[AsyncGeneratorFunction: frstcmfrstsvd]
item =  { value: 'x', index: 1, status: 'fulfilled' }
item =  { value: 'y', index: 3, status: 'fulfilled' }
item =  { value: 'z', index: 5, status: 'fulfilled' }
item =  { value: 'b', index: 2, status: 'fulfilled' }
item =  { value: 'a', index: 0, status: 'fulfilled' }
item =  { value: 'c', index: 4, status: 'fulfilled' }
Here is an example of how has to behave when there are rejections:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import frstcmfrstsvd from 'frstcmfrstsvd';
// See https://stackoverflow.com/questions/40920179/should-i-refrain-from-handling-promise-rejection-asynchronously
process.on('rejectionHandled', () => { });
process.on('unhandledRejection', error => {
    console.log('unhandledRejection');
});
const sleep = time => 
   new Promise(resolve => setTimeout(resolve, time));
const arr = [
    sleep(2000).then(() => 'a'),
    'x',
    sleep(1000).then(() => 'b'),
    'y',
    sleep(3000).then(() => { throw `Ohhh:\n` }),
    'z',
];
(async () => {
    try {
        for await (let item of frstcmfrstsvd(arr)) {
            console.log("item = ",item);
        }
    } catch(e) {
       console.log('Catched!:\n', e);
    }
})()
Gives as output:
1
2
3
4
5
6
7
➜  firstcomefirstserved git:(main) ✗ node examples/reject-frstcmfrstsvd.mjs 
item =  { value: 'x', index: 1, status: 'fulfilled' }
item =  { value: 'y', index: 3, status: 'fulfilled' }
item =  { value: 'z', index: 5, status: 'fulfilled' }
item =  { value: 'b', index: 2, status: 'fulfilled' }
item =  { value: 'a', index: 0, status: 'fulfilled' }
item =  { reason: 'Ohhh:\n', index: 4, status: 'rejected' }
Write a program to compare the performance of your solution with the performance of Promise.allSettled.
1
2
3
4
5
6
7
8
9
 ➜  firstcomefirstserved git:(main) ✗ node examples/performance-reject-frstcmfrstsvd.mjs
frstcmfrstsvd: 320.399ms
allsettled: 329.469ms
➜  firstcomefirstserved git:(main) ✗ node examples/performance-reject-frstcmfrstsvd.mjs
frstcmfrstsvd: 323.915ms
allsettled: 331.516ms
➜  firstcomefirstserved git:(main) ✗ node examples/performance-reject-frstcmfrstsvd.mjs
frstcmfrstsvd: 324.116ms
allsettled: 331.935ms