18

Doesn't code take an efficiency hit by being synchronous? Why is coding synchronously a win? I found these two links in doing some research: http://bjouhier.wordpress.com/2012/03/11/fibers-and-threads-in-node-js-what-for/, https://github.com/Sage/streamlinejs/

If the goal is to prevent spaghetti code, then clearly you can have asynchronous code, with streamline.js for example, that isn't a callback pyramid, right?

Matt
  • 5,553
  • 5
  • 24
  • 32

2 Answers2

33

You have to distinguish two things here:

  • Synchronous functions like node's fs.readFileSync, fs.statSync, etc. All these functions have a Sync in their names (*). These functions are truly synchronous and blocking. If you call them, you block the event loop and you kill node's performance. You should only use these functions in your server's initialization script (or in command-line scripts).
  • Libraries and tools like fibers or streamline.js. These solutions allow you to write your code in sync-style but the code that you write with them will still execute asynchronously. They do not block the event loop.

(*) require is also blocking.

Meteor uses fibers. Its code is written in sync-style but it is non-blocking.

The win is not on the performance side (these solutions have their own overhead so they may be marginally slower but they can also do better than raw callbacks on specific code patterns like caching). The win, and the reason why these solutions have been developed, is on the usability side: they let you write your code in sync-style, even if you are calling asynchronous functions.

Jan 25 2017 edit: I created 3 gists to illustrate non-blocking fibers: fibers-does-not-block.js, fibers-sleep-sequential.js, fibers-sleep-parallel.js

Bruno Jouhier
  • 1,013
  • 9
  • 11
  • "They do not block the event loop." The statement is misleading as fibers have to block the event loop in critical sections. It is up to developer to decide what the critical sections are and use fibers appropriately. – ashim Jan 18 '17 at 23:14
  • @ashim. This is wrong: fibers are non-preemptive; context switches between fibers are implemented by libcoro and are completely decoupled from the event loop. – Bruno Jouhier Jan 20 '17 at 09:55
  • I am confused. If I put i/o operations inside a fiber, between fiber.run() fiber.yield() commands, then the rest of the program is going to wait until that section of code executed. How it is not blocking? Am I misunderstanding something? – ashim Jan 22 '17 at 17:37
  • Your fiber will start the I/O operation and then call `Fiber.yield()`. This yield will save the context and transfer control to the main loop which will process all the callbacks, as usual. When your I/O callback eventually fires, it will call `fiber.run()` from the main loop to resume execution of your fiber where it yielded before. The event loop is never blocked; it runs while your code _appears_ to block on `Fiber.yield()`. Same thing with `async/await`: main loop runs everywhere your code _awaits_. – Bruno Jouhier Jan 23 '17 at 20:27
  • I created a small gist to illustrate callbacks firing during `Fiber.yield()`: https://gist.github.com/bjouhier/7d660269b6fae13b7e33081d391c665d – Bruno Jouhier Jan 24 '17 at 22:21
  • That is a great example! You might consider adding it to your original answer. Other people will benefit too. One more question though. What if we have 2 update methods to a mongodb (https://docs.mongodb.com/manual/reference/method/db.collection.update/). If we do fiber.run() only in the callback, then we block execution only after the db has been modified. Such situation is a race condition. How can fibers help here? – ashim Jan 25 '17 at 04:40
  • I don't understand what you mean by "race condition". I've created 2 more gists: one for sequential execution: https://gist.github.com/bjouhier/ab384311dc10d67e2599b367eecc3fa2 and one for parallel execution: https://gist.github.com/bjouhier/b26e69ff5ecec58dd8d6e62c5d3ebcc4. Notice how the "tick" keeps running in both cases. If you have several fibers that access a shared resource, you may need a synchronization primitive. For this I'm using a small "funnel" utility. See https://github.com/Sage/f-promise/blob/master/README.md#funnel – Bruno Jouhier Jan 25 '17 at 08:25
3

The code is not "synchronous" when using something like streamlinejs. The actual code will still run asynchronously. It's not very pretty to write lots of anonymous callback functions, thats where these things helps.

Chris
  • 4,204
  • 1
  • 25
  • 30