> For the complete documentation index, see [llms.txt](https://strctr.gitbook.io/programming/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://strctr.gitbook.io/programming/01-languages/javascript/01-language/e-controls/e4-promises/07-promise-composition.md).

# 07. Promise Composition

Существует две системных функции для комбинирования Promise:

* `Promise.all` соответствует приему "для всех".
* `Promise.race` соответствует принципу winner takes it all.

## `Promise.all`

Метод `Promise.all(array: Promise)` принимает на вход массив Promise и возвращает новый Promise, который будет выполнен когда пока все входящие в него Promise будут выполнены.

* `Promise.all()` и возвращает массив результатов.
* Note that even if a single dependency is rejected, the `Promise.all` method will be rejected entirely as well.
* If an empty iterable is passed, then this method returns (synchronously) an already resolved promise.
* If all of the passed-in promises fulfill, or are not promises, the promise returned by `Promise.all()` is fulfilled asynchronously.
* If any of the passed-in promises reject, `Promise.all` asynchronously rejects with the value of the promise that rejected, whether or not the other promises have resolved.

```javascript
const p1 = Promise.resolve(3);
const p2 = 1337;
const p3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, "foo");
});

Promise.all([p1, p2, p3]).then(values => {
  console.log(values); // [3, 1337, "foo"]
});
```

`Promise.all` is good for executing many promises at once:

```javascript
Promise.all([promise1, promise2]);
```

## `Promise.race`

The `Promise.race(iterable)` method returns a promise that resolves or rejects as soon as one of the promises in the iterable resolves or rejects, with the value or reason from that promise.

* Результатом будет только первый settled Promise из списка (неважно как - успешно или неуспешно).
* Успешным считается любой Promise, даже если он был rejected.
* Остальные игнорируются.
* If the iterable passed is empty, the promise returned will be forever pending.

```javascript
// we are passing as argument an array of promises that are already resolved,
// to trigger Promise.race as soon as possible
var resolvedPromisesArray = [Promise.resolve(33), Promise.resolve(44)];

var p = Promise.race(resolvedPromisesArray);
// immediately logging the value of p
console.log(p);

// using setTimeout we can execute code after the stack is empty
setTimeout(function() {
  console.log("the stack is now empty");
  console.log(p);
});

// logs, in order:
// Promise { <state>: "pending" }
// the stack is now empty
// Promise { <state>: "fulfilled", <value>: 33 }
```

`Promise.race` is good for setting a timeout:

```javascript
Promise.race([
  new Promise((resolve, reject) => {
    setTimeout(reject, 10000); // timeout after 10 secs
  }),
  doSomethingThatMayTakeAwhile()
]);
```

## Promise.allSettled

`Promise.allSettled` returns a promise that’s fulfilled with an array of promise state snapshots, but only after all the original promises have settled; i.e. become either fulfilled or rejected.

```javascript
const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
const promises = [promise1, promise2];

Promise.allSettled(promises).then((results) => console.log(results));

/**
 * [
 *   { status: "fulfilled", value: 3 }, 
 *   { status: "rejected", reason: "foo" }
 * ]
 */

```

## Sequential promise execution

One solution is to run the Promises in series, or one after the other. Unfortunately there’s no simple analog to `Promise.all` in ES6 (why?), but `Array.reduce` can help us:

```javascript
let itemIDs = [1, 2, 3, 4, 5];

itemIDs.reduce((promise, itemID) => {
  return promise.then(_ => api.deleteItem(itemID));
}, Promise.resolve());
```

Такое использование `reduce` гарантирует, что then будут вызываться последовательно, один за другим, формируя последовательное исполнение. Можно использовать `forEach`, но это будет случайное исполнение:

```javascript
itemIDs.forEach(itemID => {
  api.deleteItem(itemID);
});
```

## Serial queue of events

It is possible to imitate serial queue of events using promises:

```javascript
let queue = Promise.resolve();
function serialQueue(nexTask) {
  queue = queue
    .catch(() => {}) // to catch error from previous task
    .then(nextTask);
  return queue;
}
```

## Dynamic chains

Sometimes we want to construct our Promise chain dynamically, e.g. inserting an extra step if a particular condition is met. Be sure to update the value of promise by writing `promise = promise.then(/*...*/)`.

```javascript
function readFileAndMaybeLock(filename, createLockFile) {
  let promise = Promise.resolve();

  if (createLockFile) {
    promise = promise.then(_ => writeFilePromise(filename + ".lock", ""));
  }

  return promise.then(_ => readFilePromise(filename));
}
```

## Passing data between Promise callbacks

The following code illustrates a common problem with Promise callbacks: The variable connection (line A) exists in one scope, but needs to be accessed in other scopes (line B, line C).

```javascript
db.open()
.then(connection => { // (A)
    return connection.select({ name: 'Jane' });
})
.then(result => {
    // Process result
    // Use `connection` to make more queries (B)
})
···
.catch(error => {
    // handle errors
})
.finally(() => {
    connection.close(); // (C)
});
```

We can solve this problem with Promises if we nest Promise chains:

```javascript
db.open() // (A)
.then(connection => { // (B)
    return connection.select({ name: 'Jane' }) // (C)
        .then(result => {
            // Process result
            // Use `connection` to make more queries
        })
        ···
        .catch(error => {
            // handle errors
        })
        .finally(() => {
            connection.close();
        });
})
```

There are two Promise chains:

* The first Promise chain starts in line `A`. connection is the asynchronously delivered result of `open()`.
* The second Promise chain is nested inside the `.then()` callback starting in line `B`. It starts in line `C`. Note the return in line `C`, which ensures that both chains are eventually merged correctly.

The nesting gives all callbacks in the second chain access to connection.

Another solutions for the same problem:

* multiple return values
* local variable and side effects (worst scenario)
* `async`/`await`

```javascript
const connection = await db.open();
try {
    const result = await connection.select({ name: 'Jane' });
    ···
} catch (error) {
    // handle errors
} finally {
    connection.close();
}
```
