When we write programs, it is common to need to keep track of some form of state during the execution of the program. State refers to any data that can change as the program runs. For example, in a counter application, the count value changes as the user increments or decrements it. Similarly, in a banking application, the account balance changes as deposits and withdrawals are made. State management is crucial to building interactive and dynamic applications.
In traditional imperative programming, one common way to store state is using variables. However, this approach can introduce bugs, especially when the state is shared between multiple components or functions. As the program becomes more complex, managing shared state can become challenging.
To overcome these issues, Effect introduces a powerful data type called Ref, which represents a mutable reference. With Ref, we can share state between different parts of our program without relying on mutable variables directly. Instead, Ref provides a controlled way to handle mutable state and safely update it in a concurrent environment.
Effect’s Ref data type enables communication between different fibers in your program. This capability is crucial in concurrent programming, where multiple tasks may need to access and update shared state simultaneously.
In this guide, we will explore how to use the Ref data type to manage state in your programs effectively. We will cover simple examples like counting, as well as more complex scenarios where state is shared between different parts of the program. Additionally, we will show how to use Ref in a concurrent environment, allowing multiple tasks to interact with shared state safely.
Let’s dive in and see how we can leverage Ref for effective state management in your Effect programs.
Using Ref
Here is a simple example using Ref to create a counter:
Example (Basic Counter with Ref)
1
import {
import Effect
@since ― 2.0.0
@since ― 2.0.0
@since ― 2.0.0
Effect,
import Ref
Ref } from"effect"
2
3
class
classCounter
Counter {
4
Counter.inc: Effect.Effect<void, never, never>
inc:
import Effect
@since ― 2.0.0
@since ― 2.0.0
@since ― 2.0.0
Effect.
interfaceEffect<outA, outE=never, outR=never>
The Effect interface defines a value that describes a workflow or job,
which can succeed or fail.
Details
The Effect interface represents a computation that can model a workflow
involving various types of operations, such as synchronous, asynchronous,
concurrent, and parallel interactions. It operates within a context of type
R, and the result can either be a success with a value of type A or a
failure with an error of type E. The Effect is designed to handle complex
interactions with external resources, offering advanced features such as
fiber-based concurrency, scheduling, interruption handling, and scalability.
This makes it suitable for tasks that require fine-grained control over
concurrency and error management.
To execute an Effect value, you need a Runtime, which provides the
environment necessary to run and manage the computation.
@since ― 2.0.0
@since ― 2.0.0
Effect<void>
5
Counter.dec: Effect.Effect<void, never, never>
dec:
import Effect
@since ― 2.0.0
@since ― 2.0.0
@since ― 2.0.0
Effect.
interfaceEffect<outA, outE=never, outR=never>
The Effect interface defines a value that describes a workflow or job,
which can succeed or fail.
Details
The Effect interface represents a computation that can model a workflow
involving various types of operations, such as synchronous, asynchronous,
concurrent, and parallel interactions. It operates within a context of type
R, and the result can either be a success with a value of type A or a
failure with an error of type E. The Effect is designed to handle complex
interactions with external resources, offering advanced features such as
fiber-based concurrency, scheduling, interruption handling, and scalability.
This makes it suitable for tasks that require fine-grained control over
concurrency and error management.
To execute an Effect value, you need a Runtime, which provides the
environment necessary to run and manage the computation.
@since ― 2.0.0
@since ― 2.0.0
Effect<void>
6
Counter.get: Effect.Effect<number, never, never>
get:
import Effect
@since ― 2.0.0
@since ― 2.0.0
@since ― 2.0.0
Effect.
interfaceEffect<outA, outE=never, outR=never>
The Effect interface defines a value that describes a workflow or job,
which can succeed or fail.
Details
The Effect interface represents a computation that can model a workflow
involving various types of operations, such as synchronous, asynchronous,
concurrent, and parallel interactions. It operates within a context of type
R, and the result can either be a success with a value of type A or a
failure with an error of type E. The Effect is designed to handle complex
interactions with external resources, offering advanced features such as
fiber-based concurrency, scheduling, interruption handling, and scalability.
This makes it suitable for tasks that require fine-grained control over
concurrency and error management.
To execute an Effect value, you need a Runtime, which provides the
environment necessary to run and manage the computation.
Use andThen when you need to run multiple actions in sequence, with the
second action depending on the result of the first. This is useful for
combining effects or handling computations that must happen in order.
Details
The second action can be:
A constant value (similar to
as
)
A function returning a value (similar to
map
)
A Promise
A function returning a Promise
An Effect
A function returning an Effect (similar to
flatMap
)
Note:andThen works well with both Option and Either types,
treating them as effects.
@example
// Title: Applying a Discount Based on Fetched Amount
import { pipe, Effect } from"effect"
// Function to apply a discount safely to a transaction amount
constapplyDiscount= (
total:number,
discountRate:number
):Effect.Effect<number, Error> =>
discountRate ===0
? Effect.fail(newError("Discount rate cannot be zero"))
The Effect interface defines a value that describes a workflow or job,
which can succeed or fail.
Details
The Effect interface represents a computation that can model a workflow
involving various types of operations, such as synchronous, asynchronous,
concurrent, and parallel interactions. It operates within a context of type
R, and the result can either be a success with a value of type A or a
failure with an error of type E. The Effect is designed to handle complex
interactions with external resources, offering advanced features such as
fiber-based concurrency, scheduling, interruption handling, and scalability.
This makes it suitable for tasks that require fine-grained control over
concurrency and error management.
To execute an Effect value, you need a Runtime, which provides the
environment necessary to run and manage the computation.
@since ― 2.0.0
@since ― 2.0.0
Effect<void>
5
Counter.dec: Effect.Effect<void, never, never>
dec:
import Effect
@since ― 2.0.0
@since ― 2.0.0
@since ― 2.0.0
Effect.
interfaceEffect<outA, outE=never, outR=never>
The Effect interface defines a value that describes a workflow or job,
which can succeed or fail.
Details
The Effect interface represents a computation that can model a workflow
involving various types of operations, such as synchronous, asynchronous,
concurrent, and parallel interactions. It operates within a context of type
R, and the result can either be a success with a value of type A or a
failure with an error of type E. The Effect is designed to handle complex
interactions with external resources, offering advanced features such as
fiber-based concurrency, scheduling, interruption handling, and scalability.
This makes it suitable for tasks that require fine-grained control over
concurrency and error management.
To execute an Effect value, you need a Runtime, which provides the
environment necessary to run and manage the computation.
@since ― 2.0.0
@since ― 2.0.0
Effect<void>
6
Counter.get: Effect.Effect<number, never, never>
get:
import Effect
@since ― 2.0.0
@since ― 2.0.0
@since ― 2.0.0
Effect.
interfaceEffect<outA, outE=never, outR=never>
The Effect interface defines a value that describes a workflow or job,
which can succeed or fail.
Details
The Effect interface represents a computation that can model a workflow
involving various types of operations, such as synchronous, asynchronous,
concurrent, and parallel interactions. It operates within a context of type
R, and the result can either be a success with a value of type A or a
failure with an error of type E. The Effect is designed to handle complex
interactions with external resources, offering advanced features such as
fiber-based concurrency, scheduling, interruption handling, and scalability.
This makes it suitable for tasks that require fine-grained control over
concurrency and error management.
To execute an Effect value, you need a Runtime, which provides the
environment necessary to run and manage the computation.
Use andThen when you need to run multiple actions in sequence, with the
second action depending on the result of the first. This is useful for
combining effects or handling computations that must happen in order.
Details
The second action can be:
A constant value (similar to
as
)
A function returning a value (similar to
map
)
A Promise
A function returning a Promise
An Effect
A function returning an Effect (similar to
flatMap
)
Note:andThen works well with both Option and Either types,
treating them as effects.
@example
// Title: Applying a Discount Based on Fetched Amount
import { pipe, Effect } from"effect"
// Function to apply a discount safely to a transaction amount
constapplyDiscount= (
total:number,
discountRate:number
):Effect.Effect<number, Error> =>
discountRate ===0
? Effect.fail(newError("Discount rate cannot be zero"))
Provides a way to write effectful code using generator functions, simplifying
control flow and error handling.
When to Use
Effect.gen allows you to write code that looks and behaves like synchronous
code, but it can handle asynchronous tasks, errors, and complex control flow
(like loops and conditions). It helps make asynchronous code more readable
and easier to manage.
The generator functions work similarly to async/await but with more
explicit control over the execution of effects. You can yield* values from
effects and return the final result at the end.
The console module provides a simple debugging console that is similar to the
JavaScript console mechanism provided by web browsers.
The module exports two specific components:
A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
A global console instance configured to write to process.stdout and
process.stderr. The global console can be used without importing the node:console module.
Warning: The global console object's methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. See the note on process I/O for
more information.
Example using the global console:
console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(newError('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
constname='Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console class:
constout=getStreamSomehow();
consterr=getStreamSomehow();
constmyConsole=new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(newError('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
Prints to stdout with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to printf(3)
(the arguments are all passed to util.format()).
Executes an effect and returns the result as a Promise.
Details
This function runs an effect and converts its result into a Promise. If the
effect succeeds, the Promise will resolve with the successful result. If
the effect fails, the Promise will reject with an error, which includes the
failure details of the effect.
The optional options parameter allows you to pass an AbortSignal for
cancellation, enabling more fine-grained control over asynchronous tasks.
When to Use
Use this function when you need to execute an effect and work with its result
in a promise-based system, such as when integrating with third-party
libraries that expect Promise results.
@see ― runPromiseExit for a version that returns an Exit type instead
of rejecting.
@example
// Title: Running a Successful Effect as a Promise
//Example: Handling a Failing Effect as a Rejected Promise
import { Effect } from "effect"
// Effect.runPromise(Effect.fail("my error")).catch(console.error)
// Output:
// (FiberFailure) Error: my error
@since ― 2.0.0
runPromise(
constprogram:Effect.Effect<void, never, never>
program)
28
/*
29
Output:
30
This counter has a value of 2.
31
*/
Using Ref in a Concurrent Environment
We can also use Ref in concurrent scenarios, where multiple tasks might be updating shared state at the same time.
Example (Concurrent Updates to Shared Counter)
For this example, let’s update the counter concurrently:
1
import {
import Effect
@since ― 2.0.0
@since ― 2.0.0
@since ― 2.0.0
Effect,
import Ref
Ref } from"effect"
2
13 collapsed lines
3
class
classCounter
Counter {
4
Counter.inc: Effect.Effect<void, never, never>
inc:
import Effect
@since ― 2.0.0
@since ― 2.0.0
@since ― 2.0.0
Effect.
interfaceEffect<outA, outE=never, outR=never>
The Effect interface defines a value that describes a workflow or job,
which can succeed or fail.
Details
The Effect interface represents a computation that can model a workflow
involving various types of operations, such as synchronous, asynchronous,
concurrent, and parallel interactions. It operates within a context of type
R, and the result can either be a success with a value of type A or a
failure with an error of type E. The Effect is designed to handle complex
interactions with external resources, offering advanced features such as
fiber-based concurrency, scheduling, interruption handling, and scalability.
This makes it suitable for tasks that require fine-grained control over
concurrency and error management.
To execute an Effect value, you need a Runtime, which provides the
environment necessary to run and manage the computation.
@since ― 2.0.0
@since ― 2.0.0
Effect<void>
5
Counter.dec: Effect.Effect<void, never, never>
dec:
import Effect
@since ― 2.0.0
@since ― 2.0.0
@since ― 2.0.0
Effect.
interfaceEffect<outA, outE=never, outR=never>
The Effect interface defines a value that describes a workflow or job,
which can succeed or fail.
Details
The Effect interface represents a computation that can model a workflow
involving various types of operations, such as synchronous, asynchronous,
concurrent, and parallel interactions. It operates within a context of type
R, and the result can either be a success with a value of type A or a
failure with an error of type E. The Effect is designed to handle complex
interactions with external resources, offering advanced features such as
fiber-based concurrency, scheduling, interruption handling, and scalability.
This makes it suitable for tasks that require fine-grained control over
concurrency and error management.
To execute an Effect value, you need a Runtime, which provides the
environment necessary to run and manage the computation.
@since ― 2.0.0
@since ― 2.0.0
Effect<void>
6
Counter.get: Effect.Effect<number, never, never>
get:
import Effect
@since ― 2.0.0
@since ― 2.0.0
@since ― 2.0.0
Effect.
interfaceEffect<outA, outE=never, outR=never>
The Effect interface defines a value that describes a workflow or job,
which can succeed or fail.
Details
The Effect interface represents a computation that can model a workflow
involving various types of operations, such as synchronous, asynchronous,
concurrent, and parallel interactions. It operates within a context of type
R, and the result can either be a success with a value of type A or a
failure with an error of type E. The Effect is designed to handle complex
interactions with external resources, offering advanced features such as
fiber-based concurrency, scheduling, interruption handling, and scalability.
This makes it suitable for tasks that require fine-grained control over
concurrency and error management.
To execute an Effect value, you need a Runtime, which provides the
environment necessary to run and manage the computation.
Use andThen when you need to run multiple actions in sequence, with the
second action depending on the result of the first. This is useful for
combining effects or handling computations that must happen in order.
Details
The second action can be:
A constant value (similar to
as
)
A function returning a value (similar to
map
)
A Promise
A function returning a Promise
An Effect
A function returning an Effect (similar to
flatMap
)
Note:andThen works well with both Option and Either types,
treating them as effects.
@example
// Title: Applying a Discount Based on Fetched Amount
import { pipe, Effect } from"effect"
// Function to apply a discount safely to a transaction amount
constapplyDiscount= (
total:number,
discountRate:number
):Effect.Effect<number, Error> =>
discountRate ===0
? Effect.fail(newError("Discount rate cannot be zero"))
Provides a way to write effectful code using generator functions, simplifying
control flow and error handling.
When to Use
Effect.gen allows you to write code that looks and behaves like synchronous
code, but it can handle asynchronous tasks, errors, and complex control flow
(like loops and conditions). It helps make asynchronous code more readable
and easier to manage.
The generator functions work similarly to async/await but with more
explicit control over the execution of effects. You can yield* values from
effects and return the final result at the end.
// Helper to log the counter's value before running an effect
21
const
constlogCounter: <R, E, A>(label:string, effect:Effect.Effect<A, E, R>) =>Effect.Effect<A, E, R>
logCounter= <
function (typeparameter) Rin <R, E, A>(label:string, effect:Effect.Effect<A, E, R>):Effect.Effect<A, E, R>
R,
function (typeparameter) Ein <R, E, A>(label:string, effect:Effect.Effect<A, E, R>):Effect.Effect<A, E, R>
E,
function (typeparameter) Ain <R, E, A>(label:string, effect:Effect.Effect<A, E, R>):Effect.Effect<A, E, R>
A>(
22
label: string
label:string,
23
effect: Effect.Effect<A, E, R>
effect:
import Effect
@since ― 2.0.0
@since ― 2.0.0
@since ― 2.0.0
Effect.
interfaceEffect<outA, outE=never, outR=never>
The Effect interface defines a value that describes a workflow or job,
which can succeed or fail.
Details
The Effect interface represents a computation that can model a workflow
involving various types of operations, such as synchronous, asynchronous,
concurrent, and parallel interactions. It operates within a context of type
R, and the result can either be a success with a value of type A or a
failure with an error of type E. The Effect is designed to handle complex
interactions with external resources, offering advanced features such as
fiber-based concurrency, scheduling, interruption handling, and scalability.
This makes it suitable for tasks that require fine-grained control over
concurrency and error management.
To execute an Effect value, you need a Runtime, which provides the
environment necessary to run and manage the computation.
@since ― 2.0.0
@since ― 2.0.0
Effect<
function (typeparameter) Ain <R, E, A>(label:string, effect:Effect.Effect<A, E, R>):Effect.Effect<A, E, R>
A,
function (typeparameter) Ein <R, E, A>(label:string, effect:Effect.Effect<A, E, R>):Effect.Effect<A, E, R>
E,
function (typeparameter) Rin <R, E, A>(label:string, effect:Effect.Effect<A, E, R>):Effect.Effect<A, E, R>
Provides a way to write effectful code using generator functions, simplifying
control flow and error handling.
When to Use
Effect.gen allows you to write code that looks and behaves like synchronous
code, but it can handle asynchronous tasks, errors, and complex control flow
(like loops and conditions). It helps make asynchronous code more readable
and easier to manage.
The generator functions work similarly to async/await but with more
explicit control over the execution of effects. You can yield* values from
effects and return the final result at the end.
Logs one or more messages or error causes at the current log level.
Details
This function provides a simple way to log messages or error causes during
the execution of your effects. By default, logs are recorded at the INFO
level, but this can be adjusted using other logging utilities
(Logger.withMinimumLogLevel). Multiple items, including Cause instances,
can be logged in a single call. When logging Cause instances, detailed
error information is included in the log output.
The log output includes useful metadata like the current timestamp, log
level, and fiber ID, making it suitable for debugging and tracking purposes.
This function does not interrupt or alter the effect's execution flow.
} |undefined) => <A, E, R>(self:Effect.Effect<...>) =>Effect.Effect<...> (+1overload)
Combines two effects into a single effect, producing a tuple of their
results.
Details
This function combines two effects, self and that, into one. It executes
the first effect (self) and then the second effect (that), collecting
their results into a tuple. Both effects must succeed for the resulting
effect to succeed. If either effect fails, the entire operation fails.
By default, the effects are executed sequentially. If the concurrent option
is set to true, the effects will run concurrently, potentially improving
performance for independent operations.
@see ― zipWith for a version that combines the results with a custom
function.
@see ― validate for a version that accumulates errors.
} |undefined) => <A, E, R>(self:Effect.Effect<...>) =>Effect.Effect<...> (+1overload)
Combines two effects into a single effect, producing a tuple of their
results.
Details
This function combines two effects, self and that, into one. It executes
the first effect (self) and then the second effect (that), collecting
their results into a tuple. Both effects must succeed for the resulting
effect to succeed. If either effect fails, the entire operation fails.
By default, the effects are executed sequentially. If the concurrent option
is set to true, the effects will run concurrently, potentially improving
performance for independent operations.
@see ― zipWith for a version that combines the results with a custom
function.
@see ― validate for a version that accumulates errors.
} |undefined) => <A, E, R>(self:Effect.Effect<...>) =>Effect.Effect<...> (+1overload)
Combines two effects into a single effect, producing a tuple of their
results.
Details
This function combines two effects, self and that, into one. It executes
the first effect (self) and then the second effect (that), collecting
their results into a tuple. Both effects must succeed for the resulting
effect to succeed. If either effect fails, the entire operation fails.
By default, the effects are executed sequentially. If the concurrent option
is set to true, the effects will run concurrently, potentially improving
performance for independent operations.
@see ― zipWith for a version that combines the results with a custom
function.
@see ― validate for a version that accumulates errors.
Logs one or more messages or error causes at the current log level.
Details
This function provides a simple way to log messages or error causes during
the execution of your effects. By default, logs are recorded at the INFO
level, but this can be adjusted using other logging utilities
(Logger.withMinimumLogLevel). Multiple items, including Cause instances,
can be logged in a single call. When logging Cause instances, detailed
error information is included in the log output.
The log output includes useful metadata like the current timestamp, log
level, and fiber ID, making it suitable for debugging and tracking purposes.
This function does not interrupt or alter the effect's execution flow.
Executes an effect and returns the result as a Promise.
Details
This function runs an effect and converts its result into a Promise. If the
effect succeeds, the Promise will resolve with the successful result. If
the effect fails, the Promise will reject with an error, which includes the
failure details of the effect.
The optional options parameter allows you to pass an AbortSignal for
cancellation, enabling more fine-grained control over asynchronous tasks.
When to Use
Use this function when you need to execute an effect and work with its result
in a promise-based system, such as when integrating with third-party
libraries that expect Promise results.
@see ― runPromiseExit for a version that returns an Exit type instead
of rejecting.
@example
// Title: Running a Successful Effect as a Promise
Provides a way to write effectful code using generator functions, simplifying
control flow and error handling.
When to Use
Effect.gen allows you to write code that looks and behaves like synchronous
code, but it can handle asynchronous tasks, errors, and complex control flow
(like loops and conditions). It helps make asynchronous code more readable
and easier to manage.
The generator functions work similarly to async/await but with more
explicit control over the execution of effects. You can yield* values from
effects and return the final result at the end.
Provides a way to write effectful code using generator functions, simplifying
control flow and error handling.
When to Use
Effect.gen allows you to write code that looks and behaves like synchronous
code, but it can handle asynchronous tasks, errors, and complex control flow
(like loops and conditions). It helps make asynchronous code more readable
and easier to manage.
The generator functions work similarly to async/await but with more
explicit control over the execution of effects. You can yield* values from
effects and return the final result at the end.
Provides a way to write effectful code using generator functions, simplifying
control flow and error handling.
When to Use
Effect.gen allows you to write code that looks and behaves like synchronous
code, but it can handle asynchronous tasks, errors, and complex control flow
(like loops and conditions). It helps make asynchronous code more readable
and easier to manage.
The generator functions work similarly to async/await but with more
explicit control over the execution of effects. You can yield* values from
effects and return the final result at the end.
The console module provides a simple debugging console that is similar to the
JavaScript console mechanism provided by web browsers.
The module exports two specific components:
A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
A global console instance configured to write to process.stdout and
process.stderr. The global console can be used without importing the node:console module.
Warning: The global console object's methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. See the note on process I/O for
more information.
Example using the global console:
console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(newError('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
constname='Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console class:
constout=getStreamSomehow();
consterr=getStreamSomehow();
constmyConsole=new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(newError('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
Prints to stdout with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to printf(3)
(the arguments are all passed to util.format()).
Provides a way to write effectful code using generator functions, simplifying
control flow and error handling.
When to Use
Effect.gen allows you to write code that looks and behaves like synchronous
code, but it can handle asynchronous tasks, errors, and complex control flow
(like loops and conditions). It helps make asynchronous code more readable
and easier to manage.
The generator functions work similarly to async/await but with more
explicit control over the execution of effects. You can yield* values from
effects and return the final result at the end.
Dynamically provides an implementation for a service using an effect.
Details
This function allows you to provide an implementation for a service
dynamically by using another effect. The provided effect is executed to
produce the service implementation, which is then made available to the
consuming effect. This is particularly useful when the service implementation
itself requires asynchronous or resource-intensive initialization.
For example, you can use this function to lazily initialize a database
connection or fetch configuration values from an external source before
making the service available to your effect.
Executes an effect and returns the result as a Promise.
Details
This function runs an effect and converts its result into a Promise. If the
effect succeeds, the Promise will resolve with the successful result. If
the effect fails, the Promise will reject with an error, which includes the
failure details of the effect.
The optional options parameter allows you to pass an AbortSignal for
cancellation, enabling more fine-grained control over asynchronous tasks.
When to Use
Use this function when you need to execute an effect and work with its result
in a promise-based system, such as when integrating with third-party
libraries that expect Promise results.
@see ― runPromiseExit for a version that returns an Exit type instead
of rejecting.
@example
// Title: Running a Successful Effect as a Promise
//Example: Handling a Failing Effect as a Rejected Promise
import { Effect } from "effect"
// Effect.runPromise(Effect.fail("my error")).catch(console.error)
// Output:
// (FiberFailure) Error: my error
@since ― 2.0.0
runPromise(
construnnable:Effect.Effect<void, never, never>
runnable)
47
/*
48
Output:
49
MyState has a value of 2.
50
*/
Note that we use Effect.provideServiceEffect instead of Effect.provideService to provide an actual implementation of the MyState service because all the operations on the Ref data type are effectful, including the creation Ref.make(0).
Sharing State Between Fibers
You can use Ref to manage shared state between multiple fibers in a concurrent environment.
Example (Managing Shared State Across Fibers)
Let’s look at an example where we continuously read names from user input until the user enters "q" to exit.
First, let’s introduce a readLine utility to read user input (ensure you have @types/node installed):
The Effect interface defines a value that describes a workflow or job,
which can succeed or fail.
Details
The Effect interface represents a computation that can model a workflow
involving various types of operations, such as synchronous, asynchronous,
concurrent, and parallel interactions. It operates within a context of type
R, and the result can either be a success with a value of type A or a
failure with an error of type E. The Effect is designed to handle complex
interactions with external resources, offering advanced features such as
fiber-based concurrency, scheduling, interruption handling, and scalability.
This makes it suitable for tasks that require fine-grained control over
concurrency and error management.
To execute an Effect value, you need a Runtime, which provides the
environment necessary to run and manage the computation.
Creates an Effect that represents an asynchronous computation guaranteed to
succeed.
Details
The provided function (thunk) returns a Promise that should never reject; if it does, the error
will be treated as a "defect".
This defect is not a standard error but indicates a flaw in the logic that
was expected to be error-free. You can think of it similar to an unexpected
crash in the program, which can be further managed or logged using tools like
catchAllDefect
.
Interruptions
An optional AbortSignal can be provided to allow for interruption of the
wrapped Promise API.
When to Use
Use this function when you are sure the operation will not reject.
@see ― tryPromise for a version that can handle failures.
new <string>(executor: (resolve: (value:string|PromiseLike<string>) =>void, reject: (reason?:any) =>void) =>void) =>Promise<string>
Creates a new Promise.
@param ― executor A callback used to initialize the promise. This callback is passed two arguments:
a resolve callback used to resolve the promise with a value or the result of another promise,
and a reject callback used to reject the promise with a provided reason or error.
The readline.createInterface() method creates a new readline.Interface instance.
import readline from'node:readline';
constrl= readline.createInterface({
input: process.stdin,
output: process.stdout,
});
Once the readline.Interface instance is created, the most common case is to
listen for the 'line' event:
rl.on('line', (line) => {
console.log(`Received: ${line}`);
});
If terminal is true for this instance then the output stream will get
the best compatibility if it defines an output.columns property and emits
a 'resize' event on the output if or when the columns ever change
(process.stdout does this automatically when it is a TTY).
When creating a readline.Interface using stdin as input, the program
will not terminate until it receives an EOF character. To exit without
waiting for user input, call process.stdin.unref().
The process.stdin property returns a stream connected tostdin (fd 0). It is a net.Socket (which is a Duplex stream) unless fd 0 refers to a file, in which case it is
a Readable stream.
For details of how to read from stdin see readable.read().
As a Duplex stream, process.stdin can also be used in "old" mode that
is compatible with scripts written for Node.js prior to v0.10.
For more information see Stream compatibility.
In "old" streams mode the stdin stream is paused by default, so one
must call process.stdin.resume() to read from it. Note also that calling process.stdin.resume() itself would switch stream to "old" mode.
The process.stdout property returns a stream connected tostdout (fd 1). It is a net.Socket (which is a Duplex stream) unless fd 1 refers to a file, in which case it is
a Writable stream.
For example, to copy process.stdin to process.stdout:
import { stdin, stdout } from'node:process';
stdin.pipe(stdout);
process.stdout differs from other Node.js streams in important ways. See note on process I/O for more information.
The rl.question() method displays the query by writing it to the output,
waits for user input to be provided on input, then invokes the callback function passing the provided input as the first argument.
When called, rl.question() will resume the input stream if it has been
paused.
If the Interface was created with output set to null or undefined the query is not written.
The callback function passed to rl.question() does not follow the typical
pattern of accepting an Error object or null as the first argument.
The callback is called with the provided answer as the only argument.
An error will be thrown if calling rl.question() after rl.close().
Example usage:
rl.question('What is your favorite food? ', (answer) => {
console.log(`Oh, so your favorite food is ${answer}`);
});
Using an AbortController to cancel a question.
constac=newAbortController();
constsignal= ac.signal;
rl.question('What is your favorite food? ', { signal }, (answer) => {
console.log(`Oh, so your favorite food is ${answer}`);
});
signal.addEventListener('abort', () => {
console.log('The food question timed out');
}, { once: true });
setTimeout(() => ac.abort(), 10000);
@since ― v0.3.3
@param ― query A statement or query to write to output, prepended to the prompt.
@param ― callback A callback function that is invoked with the user's input in response to the query.
question(
message: string
message, (
answer: string
answer) => {
14
constrl:NodeReadLine.Interface
rl.
Interface.close(): void
The rl.close() method closes the Interface instance and
relinquishes control over the input and output streams. When called,
the 'close' event will be emitted.
Calling rl.close() does not immediately stop other events (including 'line')
from being emitted by the Interface instance.
The Effect interface defines a value that describes a workflow or job,
which can succeed or fail.
Details
The Effect interface represents a computation that can model a workflow
involving various types of operations, such as synchronous, asynchronous,
concurrent, and parallel interactions. It operates within a context of type
R, and the result can either be a success with a value of type A or a
failure with an error of type E. The Effect is designed to handle complex
interactions with external resources, offering advanced features such as
fiber-based concurrency, scheduling, interruption handling, and scalability.
This makes it suitable for tasks that require fine-grained control over
concurrency and error management.
To execute an Effect value, you need a Runtime, which provides the
environment necessary to run and manage the computation.
Creates an Effect that represents an asynchronous computation guaranteed to
succeed.
Details
The provided function (thunk) returns a Promise that should never reject; if it does, the error
will be treated as a "defect".
This defect is not a standard error but indicates a flaw in the logic that
was expected to be error-free. You can think of it similar to an unexpected
crash in the program, which can be further managed or logged using tools like
catchAllDefect
.
Interruptions
An optional AbortSignal can be provided to allow for interruption of the
wrapped Promise API.
When to Use
Use this function when you are sure the operation will not reject.
@see ― tryPromise for a version that can handle failures.
new <string>(executor: (resolve: (value:string|PromiseLike<string>) =>void, reject: (reason?:any) =>void) =>void) =>Promise<string>
Creates a new Promise.
@param ― executor A callback used to initialize the promise. This callback is passed two arguments:
a resolve callback used to resolve the promise with a value or the result of another promise,
and a reject callback used to reject the promise with a provided reason or error.
The readline.createInterface() method creates a new readline.Interface instance.
import readline from'node:readline';
constrl= readline.createInterface({
input: process.stdin,
output: process.stdout,
});
Once the readline.Interface instance is created, the most common case is to
listen for the 'line' event:
rl.on('line', (line) => {
console.log(`Received: ${line}`);
});
If terminal is true for this instance then the output stream will get
the best compatibility if it defines an output.columns property and emits
a 'resize' event on the output if or when the columns ever change
(process.stdout does this automatically when it is a TTY).
When creating a readline.Interface using stdin as input, the program
will not terminate until it receives an EOF character. To exit without
waiting for user input, call process.stdin.unref().
The process.stdin property returns a stream connected tostdin (fd 0). It is a net.Socket (which is a Duplex stream) unless fd 0 refers to a file, in which case it is
a Readable stream.
For details of how to read from stdin see readable.read().
As a Duplex stream, process.stdin can also be used in "old" mode that
is compatible with scripts written for Node.js prior to v0.10.
For more information see Stream compatibility.
In "old" streams mode the stdin stream is paused by default, so one
must call process.stdin.resume() to read from it. Note also that calling process.stdin.resume() itself would switch stream to "old" mode.
The process.stdout property returns a stream connected tostdout (fd 1). It is a net.Socket (which is a Duplex stream) unless fd 1 refers to a file, in which case it is
a Writable stream.
For example, to copy process.stdin to process.stdout:
import { stdin, stdout } from'node:process';
stdin.pipe(stdout);
process.stdout differs from other Node.js streams in important ways. See note on process I/O for more information.
The rl.question() method displays the query by writing it to the output,
waits for user input to be provided on input, then invokes the callback function passing the provided input as the first argument.
When called, rl.question() will resume the input stream if it has been
paused.
If the Interface was created with output set to null or undefined the query is not written.
The callback function passed to rl.question() does not follow the typical
pattern of accepting an Error object or null as the first argument.
The callback is called with the provided answer as the only argument.
An error will be thrown if calling rl.question() after rl.close().
Example usage:
rl.question('What is your favorite food? ', (answer) => {
console.log(`Oh, so your favorite food is ${answer}`);
});
Using an AbortController to cancel a question.
constac=newAbortController();
constsignal= ac.signal;
rl.question('What is your favorite food? ', { signal }, (answer) => {
console.log(`Oh, so your favorite food is ${answer}`);
});
signal.addEventListener('abort', () => {
console.log('The food question timed out');
}, { once: true });
setTimeout(() => ac.abort(), 10000);
@since ― v0.3.3
@param ― query A statement or query to write to output, prepended to the prompt.
@param ― callback A callback function that is invoked with the user's input in response to the query.
question(
message: string
message, (
answer: string
answer) => {
14
constrl:NodeReadLine.Interface
rl.
Interface.close(): void
The rl.close() method closes the Interface instance and
relinquishes control over the input and output streams. When called,
the 'close' event will be emitted.
Calling rl.close() does not immediately stop other events (including 'line')
from being emitted by the Interface instance.
Provides a way to write effectful code using generator functions, simplifying
control flow and error handling.
When to Use
Effect.gen allows you to write code that looks and behaves like synchronous
code, but it can handle asynchronous tasks, errors, and complex control flow
(like loops and conditions). It helps make asynchronous code more readable
and easier to manage.
The generator functions work similarly to async/await but with more
explicit control over the execution of effects. You can yield* values from
effects and return the final result at the end.
Executes an effect and returns the result as a Promise.
Details
This function runs an effect and converts its result into a Promise. If the
effect succeeds, the Promise will resolve with the successful result. If
the effect fails, the Promise will reject with an error, which includes the
failure details of the effect.
The optional options parameter allows you to pass an AbortSignal for
cancellation, enabling more fine-grained control over asynchronous tasks.
When to Use
Use this function when you need to execute an effect and work with its result
in a promise-based system, such as when integrating with third-party
libraries that expect Promise results.
@see ― runPromiseExit for a version that returns an Exit type instead
of rejecting.
@example
// Title: Running a Successful Effect as a Promise
Attaches callbacks for the resolution and/or rejection of the Promise.
@param ― onfulfilled The callback to execute when the Promise is resolved.
@param ― onrejected The callback to execute when the Promise is rejected.
@returns ― A Promise for the completion of which ever callback is executed.
then(
var console:Console
The console module provides a simple debugging console that is similar to the
JavaScript console mechanism provided by web browsers.
The module exports two specific components:
A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
A global console instance configured to write to process.stdout and
process.stderr. The global console can be used without importing the node:console module.
Warning: The global console object's methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. See the note on process I/O for
more information.
Example using the global console:
console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(newError('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
constname='Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console class:
constout=getStreamSomehow();
consterr=getStreamSomehow();
constmyConsole=new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(newError('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
Prints to stdout with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to printf(3)
(the arguments are all passed to util.format()).
The Effect interface defines a value that describes a workflow or job,
which can succeed or fail.
Details
The Effect interface represents a computation that can model a workflow
involving various types of operations, such as synchronous, asynchronous,
concurrent, and parallel interactions. It operates within a context of type
R, and the result can either be a success with a value of type A or a
failure with an error of type E. The Effect is designed to handle complex
interactions with external resources, offering advanced features such as
fiber-based concurrency, scheduling, interruption handling, and scalability.
This makes it suitable for tasks that require fine-grained control over
concurrency and error management.
To execute an Effect value, you need a Runtime, which provides the
environment necessary to run and manage the computation.
Creates an Effect that represents an asynchronous computation guaranteed to
succeed.
Details
The provided function (thunk) returns a Promise that should never reject; if it does, the error
will be treated as a "defect".
This defect is not a standard error but indicates a flaw in the logic that
was expected to be error-free. You can think of it similar to an unexpected
crash in the program, which can be further managed or logged using tools like
catchAllDefect
.
Interruptions
An optional AbortSignal can be provided to allow for interruption of the
wrapped Promise API.
When to Use
Use this function when you are sure the operation will not reject.
@see ― tryPromise for a version that can handle failures.
new <string>(executor: (resolve: (value:string|PromiseLike<string>) =>void, reject: (reason?:any) =>void) =>void) =>Promise<string>
Creates a new Promise.
@param ― executor A callback used to initialize the promise. This callback is passed two arguments:
a resolve callback used to resolve the promise with a value or the result of another promise,
and a reject callback used to reject the promise with a provided reason or error.
The readline.createInterface() method creates a new readline.Interface instance.
import readline from'node:readline';
constrl= readline.createInterface({
input: process.stdin,
output: process.stdout,
});
Once the readline.Interface instance is created, the most common case is to
listen for the 'line' event:
rl.on('line', (line) => {
console.log(`Received: ${line}`);
});
If terminal is true for this instance then the output stream will get
the best compatibility if it defines an output.columns property and emits
a 'resize' event on the output if or when the columns ever change
(process.stdout does this automatically when it is a TTY).
When creating a readline.Interface using stdin as input, the program
will not terminate until it receives an EOF character. To exit without
waiting for user input, call process.stdin.unref().
The process.stdin property returns a stream connected tostdin (fd 0). It is a net.Socket (which is a Duplex stream) unless fd 0 refers to a file, in which case it is
a Readable stream.
For details of how to read from stdin see readable.read().
As a Duplex stream, process.stdin can also be used in "old" mode that
is compatible with scripts written for Node.js prior to v0.10.
For more information see Stream compatibility.
In "old" streams mode the stdin stream is paused by default, so one
must call process.stdin.resume() to read from it. Note also that calling process.stdin.resume() itself would switch stream to "old" mode.
The process.stdout property returns a stream connected tostdout (fd 1). It is a net.Socket (which is a Duplex stream) unless fd 1 refers to a file, in which case it is
a Writable stream.
For example, to copy process.stdin to process.stdout:
import { stdin, stdout } from'node:process';
stdin.pipe(stdout);
process.stdout differs from other Node.js streams in important ways. See note on process I/O for more information.
The rl.question() method displays the query by writing it to the output,
waits for user input to be provided on input, then invokes the callback function passing the provided input as the first argument.
When called, rl.question() will resume the input stream if it has been
paused.
If the Interface was created with output set to null or undefined the query is not written.
The callback function passed to rl.question() does not follow the typical
pattern of accepting an Error object or null as the first argument.
The callback is called with the provided answer as the only argument.
An error will be thrown if calling rl.question() after rl.close().
Example usage:
rl.question('What is your favorite food? ', (answer) => {
console.log(`Oh, so your favorite food is ${answer}`);
});
Using an AbortController to cancel a question.
constac=newAbortController();
constsignal= ac.signal;
rl.question('What is your favorite food? ', { signal }, (answer) => {
console.log(`Oh, so your favorite food is ${answer}`);
});
signal.addEventListener('abort', () => {
console.log('The food question timed out');
}, { once: true });
setTimeout(() => ac.abort(), 10000);
@since ― v0.3.3
@param ― query A statement or query to write to output, prepended to the prompt.
@param ― callback A callback function that is invoked with the user's input in response to the query.
question(
message: string
message, (
answer: string
answer) => {
14
constrl:NodeReadLine.Interface
rl.
Interface.close(): void
The rl.close() method closes the Interface instance and
relinquishes control over the input and output streams. When called,
the 'close' event will be emitted.
Calling rl.close() does not immediately stop other events (including 'line')
from being emitted by the Interface instance.
Provides a way to write effectful code using generator functions, simplifying
control flow and error handling.
When to Use
Effect.gen allows you to write code that looks and behaves like synchronous
code, but it can handle asynchronous tasks, errors, and complex control flow
(like loops and conditions). It helps make asynchronous code more readable
and easier to manage.
The generator functions work similarly to async/await but with more
explicit control over the execution of effects. You can yield* values from
effects and return the final result at the end.
Creates a new fiber to run an effect concurrently.
Details
This function takes an effect and forks it into a separate fiber, allowing it
to run concurrently without blocking the original effect. The new fiber
starts execution immediately after being created, and the fiber object is
returned immediately without waiting for the effect to begin. This is useful
when you want to run tasks concurrently while continuing other tasks in the
parent fiber.
The forked fiber is attached to the parent fiber's scope. This means that
when the parent fiber terminates, the child fiber will also be terminated
automatically. This feature, known as "auto supervision," ensures that no
fibers are left running unintentionally. If you prefer not to have this auto
supervision behavior, you can use
forkDaemon
or
forkIn
.
When to Use
Use this function when you need to run an effect concurrently without
blocking the current execution flow. For example, you might use it to launch
background tasks or concurrent computations. However, working with fibers can
be complex, so before using this function directly, you might want to explore
higher-level functions like
raceWith
,
zip
, or others that can
manage concurrency for you.
@see ― forkWithErrorHandler for a version that allows you to handle errors.
@example
import { Effect } from"effect"
constfib= (n:number):Effect.Effect<number> =>
n <2
? Effect.succeed(n)
: Effect.zipWith(fib(n -1), fib(n -2), (a, b) => a + b)
Provides a way to write effectful code using generator functions, simplifying
control flow and error handling.
When to Use
Effect.gen allows you to write code that looks and behaves like synchronous
code, but it can handle asynchronous tasks, errors, and complex control flow
(like loops and conditions). It helps make asynchronous code more readable
and easier to manage.
The generator functions work similarly to async/await but with more
explicit control over the execution of effects. You can yield* values from
effects and return the final result at the end.
Creates a new fiber to run an effect concurrently.
Details
This function takes an effect and forks it into a separate fiber, allowing it
to run concurrently without blocking the original effect. The new fiber
starts execution immediately after being created, and the fiber object is
returned immediately without waiting for the effect to begin. This is useful
when you want to run tasks concurrently while continuing other tasks in the
parent fiber.
The forked fiber is attached to the parent fiber's scope. This means that
when the parent fiber terminates, the child fiber will also be terminated
automatically. This feature, known as "auto supervision," ensures that no
fibers are left running unintentionally. If you prefer not to have this auto
supervision behavior, you can use
forkDaemon
or
forkIn
.
When to Use
Use this function when you need to run an effect concurrently without
blocking the current execution flow. For example, you might use it to launch
background tasks or concurrent computations. However, working with fibers can
be complex, so before using this function directly, you might want to explore
higher-level functions like
raceWith
,
zip
, or others that can
manage concurrency for you.
@see ― forkWithErrorHandler for a version that allows you to handle errors.
@example
import { Effect } from"effect"
constfib= (n:number):Effect.Effect<number> =>
n <2
? Effect.succeed(n)
: Effect.zipWith(fib(n -1), fib(n -2), (a, b) => a + b)
Provides a way to write effectful code using generator functions, simplifying
control flow and error handling.
When to Use
Effect.gen allows you to write code that looks and behaves like synchronous
code, but it can handle asynchronous tasks, errors, and complex control flow
(like loops and conditions). It helps make asynchronous code more readable
and easier to manage.
The generator functions work similarly to async/await but with more
explicit control over the execution of effects. You can yield* values from
effects and return the final result at the end.
Suspends the execution of an effect for a specified Duration.
Details
This function pauses the execution of an effect for a given duration. It is
asynchronous, meaning that it does not block the fiber executing the effect.
Instead, the fiber is suspended during the delay period and can resume once
the specified time has passed.
The duration can be specified using various formats supported by the
Duration module, such as a string ("2 seconds") or numeric value
representing milliseconds.
@example
import { Effect } from"effect"
constprogram= Effect.gen(function*() {
console.log("Starting task...")
yield* Effect.sleep("3 seconds") // Waits for 3 seconds
Joins the fiber, which suspends the joining fiber until the result of the
fiber has been determined. Attempting to join a fiber that has erred will
result in a catchable error. Joining an interrupted fiber will result in an
"inner interruption" of this fiber, unlike interruption triggered by
another fiber, "inner interruption" can be caught and recovered.
Joins the fiber, which suspends the joining fiber until the result of the
fiber has been determined. Attempting to join a fiber that has erred will
result in a catchable error. Joining an interrupted fiber will result in an
"inner interruption" of this fiber, unlike interruption triggered by
another fiber, "inner interruption" can be caught and recovered.
Executes an effect and returns the result as a Promise.
Details
This function runs an effect and converts its result into a Promise. If the
effect succeeds, the Promise will resolve with the successful result. If
the effect fails, the Promise will reject with an error, which includes the
failure details of the effect.
The optional options parameter allows you to pass an AbortSignal for
cancellation, enabling more fine-grained control over asynchronous tasks.
When to Use
Use this function when you need to execute an effect and work with its result
in a promise-based system, such as when integrating with third-party
libraries that expect Promise results.
@see ― runPromiseExit for a version that returns an Exit type instead
of rejecting.
@example
// Title: Running a Successful Effect as a Promise
Attaches callbacks for the resolution and/or rejection of the Promise.
@param ― onfulfilled The callback to execute when the Promise is resolved.
@param ― onrejected The callback to execute when the Promise is rejected.
@returns ― A Promise for the completion of which ever callback is executed.
then(
var console:Console
The console module provides a simple debugging console that is similar to the
JavaScript console mechanism provided by web browsers.
The module exports two specific components:
A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
A global console instance configured to write to process.stdout and
process.stderr. The global console can be used without importing the node:console module.
Warning: The global console object's methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. See the note on process I/O for
more information.
Example using the global console:
console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(newError('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
constname='Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console class:
constout=getStreamSomehow();
consterr=getStreamSomehow();
constmyConsole=new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(newError('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
Prints to stdout with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to printf(3)
(the arguments are all passed to util.format()).