Skip to content

Put *Sync methods behind a flag in some future major version #1665

@ChALkeR

Description

@ChALkeR

No, I am not writing this from a mental hospital, please read till the bottom.

The purpose of this is to discourage using *Sync versions of the methods that could be async.

I propose to put all or at least a part of *Sync methods behind a runtime flag in some future major version of io.js and to split documentation, moving all *Sync methods to a separate page. This of course would be semver-major.

Atm, there are *Sync methods defined in zlib, fs, child_process, crypto modules and additionaly used in repl and module modules.
To allow usage inside io.js itself (i.e. for require), they could be moved to «private» methods (beginning with a _ sign).

  1. *Sync methods suggest bad practices (see 2), but when someone with full understanding of the consequences needs them, they could be constructed from userspace. See https://github.com/abbr/deasync, it creates synchronous methods from async methods.
    If deasync module is not good enough for this, this could be done when there is would be a good enough solution.

  2. When a newcomer begins writing something using io.js, he or she goes to the documentation looking how to do something, sees *Sync methods without any warnings there and almost certanly begins with using them, because that's what he or she is used to. That's easier for a newcomer than spending a few minutes reading how he or she should actually do stuff. And don't blame the newcomer, it's the presense of *Sync methods in the documentation that suggests to him or her that it's an ok way to do things. This results in a big pile of bad code by the time when the person understands that it should be rewritten. And people don't like to rewrite code for no visible reson, leaving this code to be legacy (see 3).

  3. When someone writes synchronous code (see 2) it limits how he or she can use async functions without rewriting most part of the logic, so he or she comes complaining about that there should be a *Sync version of everything out there (as the presense of *Sync versions in the core suggests it). See Is there a sync version encapsulating libmagic in node.js ? mscdex/mmmagic#32 and motivation behind https://github.com/abbr/deasync.

  4. It's completely broken either way. Even for «simple one-time scripts». See zlib: memory leak with gunzipSync #1479 — a person was doing something like files.forEach( …zlib.gzipSync(…) …), in what I suppose was a simple script. What could possibly go wrong? Memory usage has gone completely bad in his script. And even manual calls to gc() do not help. Testcase:

    'use strict';
    
    var zlib = require('zlib');
    var data = 'abcdefghijklmnopqrstuvwxyz';
    var gzipped = zlib.gzipSync(data);
    
    function call() {
      var contents = zlib.gunzipSync(gzipped);
    }
    
    for (var i = 0; i < 100000; i++) {
       call();
       if (i % 1000 === 0) {
           gc();gc();gc();gc();
           console.log(i + ' ' + JSON.stringify(process.memoryUsage()));
       }
    }
  5. People go to the doc, see *Sync versions (see 2), use them — then everyone are telling them that they are using io.js wrong just based on that fact: gripe: deprecating fs.exists/existsSync #1592 (comment).

    if you're using existsSync inside your server I'd argue you're not using io.js correctly to begin with

  6. One can argue again that using *Sync versions of methods is ok in scripts and that that's simplier, but:

    1. See 4.
    2. For one-time, throw-away scripts you can turn that flag on.
    3. Promises-based code is as clean as *Sync-based code. Promises-based code with accurate error handling is much cleaner that *Sync-based code with accurate error handling. Using Promisify won't be needed once Promises go to the core (see Feature Request: Every async function returns Promise #11).

This will require all public modules that are using *Sync methods either to rewrite things using async methods or require/include something like https://github.com/abbr/deasync to be compatible.

This can be done separately for various core modules/methods. For example, zlib.*Sync are maybe the worst of them and it looks to me that they are not actively used in public modules.

Metadata

Metadata

Assignees

No one assigned

    Labels

    discussIssues opened for discussions and feedbacks.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions