We have talked about how Effection operations allow you to bundle setup and
teardown as a unit so that automatic cleanup is _guaranteed_, but how are
they able to do this, and how can you implement your own operations that clean
up after themselves?

## Scope of a lifetime

Every operation in Effection runs in the context of an associated scope which
places a hard limit on how long it will live. For example, the script below uses
the [`spawn()`][spawn] operation to run an operation concurrently that counts to
ten, outputting one number every second. It then sleeps for five seconds before
returning;

```js
import { main, sleep, spawn } from "effection";

await main(function* () { // <- parent scope
  yield* spawn(function* () { // <- child scope
    for (let i = 1; i = 10; i++) {
      yield* sleep(1000);
      console.log(i);
    }
  });

  yield* sleep(5000);
});
```

It may surprise you to learn that this script will only output _five_
numbers, not ten. This is because the main operation completes after
just five seconds, and as a result its scope, and every scope it
contains, is halted. This in turn means that our counting operation is
shutdown and outputs nothing more.

>💡Key Concept: _no operation may outlive its scope_.

This simple rule is incredibly powerful! It means that you can create all kinds
of dynamic, concurrent processes, but the moment that they pass out of scope and
are no longer needed, they are immediately stopped. If we think about it, this
is very similar to the concept of lexical scope in JavaScript with which we're
already familiar.

```js
{
  let left = 1;
  let right = 2;
  console.log(`${left} + ${right} = ${left + right}`)
}

console.log(left) // <= ReferenceError left is not defined
```

In the snippet above, we allocate two number variables: `left` and `right`.
Inside the "curlys" we can read and write from them all we want, but once we
leave, JavaScript is going to deallocate them and the memory they hold
automatically for us and because of that, we aren't allowed to touch
them ever again. We describe this situation by saying that the variables
have "passed out of scope" and so any resources they hold (in this case
computer memory) can be automatically reclaimed.

Effection applies this very same principle to entire operations, not just
variable references. Because of this, once the outcome of an operation becomes
known, or it is no longer needed, that operation and all of the operations it
contains can be safely shut down.

## The Three Outcomes

There are only three ways an operation may pass out of scope.

1. **return** the operation completes to produce a value.
2. **error** the operation fails and exits with an exception.
3. **halt** due to a return, error or a halt of a related operation, an
operation is halted.

No matter which one of these happens, every sub-operation associated with that
operation will be automatically destroyed.

## Suspend (it's not the end)

In order to understand the lifecycle of an Operation, we must first understand the
concept of halting a Task.

In Effection, any task can be halted:

``` javascript
import { run } from 'effection';

let task = run(function*() {
  yield* suspend();
});

await task.halt();
```

Halting a Task means that its operation is canceled, and it also causes any
operation created by that operation to be halted.

## Immediate return

If an Operation is expressed as a generator (most are), we call `return()`
on the generator when that operation is halted. This
behaves somewhat similarly to if you would replace the current `yield*`
statement with a `return` statement.

Let's look at an example where a task is suspended using `yield*` with no
arguments and what happens when we call `halt` on it:

``` javascript
import { main, suspend } from 'effection';

let task = main(function*() {
  yield* suspend(); // we will "return" from here
  console.log('we will never get here');
});

await task.halt();
```

This would behave somewhat similarly to the following:

``` javascript
import { main } from 'effection';

main(function*() {
  return;
  console.log('we will never get here');
});
```

Crucially, when this happens, just like with a regular `return`, we can use
`try/finally`:

``` javascript
import { run, sleep, suspend } from 'effection';

let task = run(function*() {
  try {
    yield* suspend() // we will "return" from here
  } finally {
    console.log('yes, this will be printed!');
  }
});

await task.halt();
```

## Cleaning up

We can use this mechanism to run code as an Operation is shutting
down regardless of whether it completes successfully, is halted, or
results in an error.

Imagine that we're doing something with an HTTP server, and we're using node's
`createServer` function. To properly clean up after ourselves, we
should call `close()` on the server when we're done.

Using Effection and `try/finally`, we could do something like this:

``` javascript
import { run, suspend } from 'effection';
import { createServer } from 'http';

let task = run(function*() {
  let server = createServer();
  try {
    // in real code we would do something more interesting here
    yield* suspend();
  } finally {
    server.close();
  }
});

await task.halt();
```

## Asynchronous halt

You might be wondering what happens when we `yield*` inside the
_finally_ block. In fact, Effection handles this case for you:

``` javascript
import { run, sleep, suspend } from 'effection';

let task = run(function*() {
  try {
    yield* suspend();
  } finally {
    console.log('this task is slow to halt');
    yield* sleep(2000);
    console.log('now it has been halted');
  }
});

await task.halt();
```

While performing asynchronous operations while halting is sometimes necessary,
it is good practice to keep halting speedy and simple. We recommend avoiding
expensive operations during halt where possible, and avoiding throwing any
errors during halting.

## Ensure

Sometimes you want to avoid the rightward drift of using lots of
`try/finally` blocks.  The `ensure` operation that ships with
Effection can help you clean up this type of code.

The following behaves identically to our `try/finally` implementation above:

``` javascript
import { run, ensure } from 'effection';
import { createServer } from 'http';

let task = run(function*() {
  let server = createServer();
  yield* ensure(() => server.close());

  // in real code we would do something more interesting here
  yield* suspend();
});

await task.halt();
```

## Abort Signal

While cancellation and teardown are handled automatically for us as long as we
are using Effection operations, what do we do when we want to integrate with a
3rd party API? One very common answer is to use the JavaScript standard
[`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal)
which can broadcast an event whenever it is time for an operation to be
canceled. Effection makes it easy to create abort signals and pass them around
so that they can notify dependencies whenever an operation terminates.

To create an abort signal, we can invoke the
[`useAbortSignal()`][use-abort-signal] operation that comes with
Effection.

`AbortSignal`s instantiated with the
[`useAbortSignal()`][use-abort-signal] operation are implicitly bound
to the scope in which they were created, and whenever that task ceases
running, they will emit an `abort` event.

``` javascript
import { main, sleep, useAbortSignal } from 'effection';

await main(function*() {
  let signal = yield* useAbortSignal();

  signal.addEventListener('abort', () => console.log('done!'));

  yield* sleep(5000);
  // prints 'done!'
});
```

It is very common (though not universal) for APIs that perform
asynchronous operations to accept an `AbortSignal` to make
sure those operations go away if needed. For example, the standard
[`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch) function
accepts an abort signal to cancel itself when needed.

``` javascript
function* request(url) {
  let signal = yield* useAbortSignal();
  let response = yield* fetch('/some/url', { signal });
  if (response.ok) {
    return yield response.text();
  } else {
    throw new Error(`failed: ${ response.status }: ${response.statusText}`);
  }
}
```

Now, no matter what happens, when the `request` operation is completed (or
canceled), the HTTP request is guaranteed to be shut down.

## Embedding API

The nice thing about scope is that you don't need to worry about it. It's just
there, ensuring that things get cleaned up as soon as they are no longer needed.
Sometimes, however, it is necessary to interact with the scope of an operation
from outside of Effection in normal JavaScript execution. To do this we use the
[Scope][scope] API. Once we have a reference to a `Scope`, we can use it to run
operations as though they were being spawned directly from an operation running
in that scope. An example of this might be to spawn a new operation for each
request to an express server.

Express handles the http request, but it does not know anything about
Effection, and so we have to explicitly connect each request to an
operation.

```js
import { until, ensure, main, useScope, useAbortSignal, suspend } from "effection";
import express from "express";

await main(function*() {
  // capture a reference to the current scope.
  let scope = yield* useScope();

  express().get("/", async (req, res) => {
    // use the scope to spawn an operation within it.
    return await scope.run(function*() {
      let signal = yield* useAbortSignal();
      let response = yield* until(fetch(`https://google.com?q=${req.params.q}`, { signal }));
      res.send(yield* until(response.text()));
    });
  });

  let server = express.listen();

  yield* ensure(() => server.close());

  yield* suspend();
});
```

And, because every request runs inside of our main scope when that scope
exits because someone hit `CTRL-C`, then any in-flight requests will be
canceled.

The other way to get a reference to a [`Scope`][scope] is to create a completely
new one from scratch. This can be done with the [`createScope()`][create-scope]
function. This will create a fresh scope that is completely disconnected from
any other scope.

One common use of [`createScope()`][create-scope] is for running operations in
test cases.

```js
let scope;
let destroy;

beforeEach(() => {
  [scope, destroy] = createScope();
});

it("does something", async () => {
  await scope.run(function* () {
   // run operations in test case
  });
});

afterEach(async () => {
  await destroy();
});
```

Whenever you use `createScope()`, it is important that you `await` the
destruction operation when the scope is no longer needed since it will not
be destroyed implicitly.

[spawn]: /api/v3/spawn
[use-abort-signal]: /api/v3/useAbortSignal
[scope]: /api/v3/Scope
[create-scope]: /api/v3/createScope
