-
Notifications
You must be signed in to change notification settings - Fork 0
03 Async
All methods have asynchronous and synchronous forms available.
Synchronous forms throw exceptions immediately. Can use try/catch to handle exceptions or allow them to bubble up.
Asynchronous form always takes a completion callback as its last argument. The arguments passed to the completion callback depend on the method, but the first argument is always reserved for an exception. If the operation was completed successfully, then the first argument will be null or undefined.
No guaranteed ordering, prone to errors. To create ordering, chain the callbacks.
For example, if you want to execute fs.stat before fs.rename.
fs.rename('/tmp/hello', '/tmp/world', (err) => {
if (err) throw err;
fs.stat('/tmp/world', (err, stats) => {
if (err) throw err;
console.log(`stats: ${JSON.stringify(stats)}`);
});
});
In busy processes, the programmer is strongly encouraged to use the asynchronous versions. The synchronous versions will block the entire process until they complete -- halting all connections.
The relative path to a filename can be used. However, that this path will be relative to process.cwd().
fs functions support passing and receiving paths as both strings and Buffers. Buffers is intended to make it possible to work with filesystems that allow for non-UTF-8 filenames. The string API converts to and from UTF-8 automatically.
On certain file systems (such as NTFS and HFS+), filenames are always encoded as UTF-8. Passing non-UTF-8 encoded Buffers to fs functions will not work as expected on these file systems.
Callbacks enable a balanced, non-blocking flow of asynchronous control across modules and applications. But at scale, you need a common, reliable protocol. The "error-first" callback ("errorback", "errback", or "node-style callback") has become the standard for Node.js callbacks.
Node.js uses callbacks today using Continuation-Passing Style (CPS. In CPS, a "continuation function" (callback) is passed as an argument and is called once the rest of the code has been run. Allows different functions to asynchronously hand control back and forth across an application.
Node.js relies on asynchronous code for speed, a dependable callback pattern is crucial.
Rules:
- The first argument of the callback is reserved for an error object.
- If an error occurred, it will be returned by the first
errargument.
- If an error occurred, it will be returned by the first
- The second argument of the callback is reserved for any successful response data.
- If no error occurred,
errwill be set to null and any successful data will be returned in the second argument.
- If no error occurred,
If something goes wrong, the first argument err will be populated with an error object containing information about the problem. How you handle this error is up to you. You can throw if you want your entire application to shutdown or you can propagate that error out to the next callback.
When a function passes its errors to a callback it no longer has to make assumptions on how that error should be handled.
When you're consistent with this pattern, errors can be propagated up as many times as you'd like. Each callback can choose to ignore, handle, or propagate the error based on the information and context that exist at that level.
With a solid callback protocol in hand, callbacks can be called in parallel, in a queue, in serial, or any other combination you can imagine.
Mocking is a technique to isolate test subjects by replacing dependencies with objects that you can control and inspect. A dependency can be anything your subject depends on, but it is typically a module that the subject imports.
For JavaScript, there are great mocking libraries available like testdouble and sinon, and Jest provides mocking out of the box.
When talking about mocking in Jest, we're typically talking about replacing dependencies with the Mock Function.
The goal for mocking to replace something we don't control with something we do, it's important that what we replace it with has all the features we need.
The Mock Function provides features to:
- Capture calls
- Set return values
- Change the implementation
The simplest way to create a Mock Function instance is with jest.fn().
One of the common ways to use the Mock Function is by passing it directly as an argument to the function you are testing. Allows you to run your test subject, then assert how the mock was called and with what arguments:
const doAdd = (a, b, callback) => {
callback(a + b);
};
test("calls callback with arguments added", () => {
const mockCallback = jest.fn();
doAdd(1, 2, mockCallback);
expect(mockCallback).toHaveBeenCalledWith(3);
});
This strategy is solid, but it requires that your code supports dependency injection. If this is not the case, so we will need tools to mock existing modules and functions instead.
There are three main types of module and function mocking in Jest:
-
jest.fn: Mock a function -
jest.mock: Mock a module -
jest.spyOn: Spy or mock a function
Each of these, in some way, create the Mock Function.