# 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();
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://strctr.gitbook.io/programming/01-languages/javascript/01-language/e-controls/e4-promises/07-promise-composition.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
