Exploring the power of ES6 Generator functions in conjunction with SailsJS

Utilizing generators in nodejs is a game changer for me. They really enhance the server-side feel of my code. I've been experimenting with incorporating generators into my Sails app. Here's an example where my controller successfully works when I visit 'get /hi':

/**
 * FooController
 *
 * @description :: Server-side logic for managing foos
 * @help        :: See http://sailsjs.org/#!/documentation/concepts/Controllers
 */

module.exports = {
  hi: function (req, res){
    return res.send("Hi there!");
  }
};

However, as soon as I convert the hi action to a generator function...

module.exports = {
  hi: function* (req, res){
    return res.send("Hi there!");
  }
};

the response never gets returned. It seems like it's stuck waiting for something. How can ES6 generators be effectively used within SailsJS controllers and throughout the entire Sails framework?

Answer №1

Utilizing this method can greatly enhance code readability and flexibility, but it requires a specific approach.

As mentioned by @Cohars, Sails expects controller actions to be functions. While you can't directly pass a generator like Koa, there are workarounds available. Essentially, a generator on its own is not very useful; you need a function to call and iterate it. Koa handles this at the framework level, but at the library level, options like co or yortus/asyncwait/ provide similar functionality. Personally, I prefer using yortus/asyncawait/ due to its flexibility with Node fibers being implemented as functions compared to ES6 generators.

Here's how to implement it:

Start by running npm install co and require it in your controller or add it globally in config/bootstrap.js for frequent use. Once setup is complete, integrate it into your controllers.

module.exports = {
  hi: function(req, res){
    co(function* (){
      // You can incorporate it with model calls like this
      let user = yield User.findOne(req.param('id'))
      return res.send("Hello " + user.name + "!");
    })
  }
};

That's the basic implementation.

If you prefer using async/await, the process is quite similar:

module.exports = {
  hi: function(req, res){
    async(function(){
      let userName = await(User.findOne(req.param('id'))).name
      return res.send("Hi there " + userName + "!");
    })();
  }
};

A point to note is that when calling other methods of your controller using this, ensure to preserve the context since it will reference the generator function, or the regular function if you're utilizing async/await. Using fat arrows with async/await can help maintain contexts efficiently.

I've been utilizing the second approach for several months now, finding success with both methods. The async/await module appears slightly faster and aligns well with CoffeeScript usage.

UPDATE:

An async-handler wrapper has been developed to complement yortus async/await functionality. Feel free to modify it for compatibility with other libraries if needed.

You can access the wrapper at https://www.npmjs.com/package/async-handler. It's currently in alpha, but I've tested it in my applications with positive results. Should you encounter any issues, kindly submit them for resolution.

With the async-handler in place, errors related to async/promise handling propagate correctly within Sails unless caught by a try/catch block.

Answer №2

Sails is looking for a standard function instead of a generator in this case. Consider exploring co if you are unsure whether it would be beneficial for Sails. However, if you are determined to use generators, perhaps give Koa a try as it has various frameworks built upon it.

Answer №3

This is my approach to achieve it:

const Promise = require('bluebird');

module.exports = {
  greet: Promise.coroutine(function* (req, res) {
    let id = req.params('id'),
        userName;
    try {
      userName = yield User.findOne(id).name;
    }
    catch (e) {
      sails.log.error(e);
      return res.serverError(e.message);
    }

    return res.ok(`Hello ${userName}!`);
  })
}

It works smoothly. Just make sure that the functions called from your controllers return promises.

Answer №4

Add the following snippet to your bootstrap.js, and watch everything fall into place effortlessly!

var _ = require('lodash');
var coExpress = require('co-express');

sails.modules.loadControllers(function (err, modules) {
  if (err) {
    return callback(err);
  }
  sails.controllers = _.merge(sails.controllers, modules);

  // Enhancing every action of all controllers
  _.each(sails.controllers, function(controller, controllerId) {
    _.each(controller, function(action, actionId) {
      actionId = actionId.toLowerCase();
      console.log('Enhancing route:', controllerId, actionId);
      // Using co.wrap, convert generator functions to callback functions
      action = coExpress(action);
      sails.hooks.controllers.middleware[controllerId][actionId] = action;
    });
  });
  // Reload routes
  sails.router.load(function () {
    // Reload blueprints
    sails.hooks.blueprints.initialize(function () {
      sails.hooks.blueprints.extendControllerMiddleware();
      sails.hooks.blueprints.bindShadowRoutes();
      callback();
    });
  });
});

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the reason for NextJS web server to listen on additional TCP ports?

When I create a simple NextJS project using npx create-next-app@latest and run it with "npm run dev" (or build it with "npm run build" and then start it with "npm start"), I noticed in Process Explorer - SysInternals that there are multiple node.exe proces ...

Updating user data when logged in on multiple browsers can be tricky. Here's how to make sure that

I am currently using the express-session middleware to store user sessions in a Mongo collection. What is the most effective way to update all user sessions when changes are made from one session? For instance, if a user updates their email in session A, ...

Nodemailer seems to be having trouble delivering emails at the moment

I've configured the transformer like this: var transporter = nodemailer.createTransport({ service: "Gmail", host: "smtp.gmail.com", port: 587, secure: false, auth: { user: process.env.GMAIL, pass: process.env.GPASSWORD, }, }); Her ...

Using Node.js to convert an array into a string and then appending slashes onto it

Currently, I am in the process of learning nodeJS. Despite trying various Stack Overflow questions and answers, I have been unable to find a solution. I have been stuck on this issue for the past two days. Here is my question: Angular JS code: $scope.q ...

Issue with holding session in Mongoose-Auth/Express/Connect not being resolved

Currently, I am in the process of developing a node js app and attempting to implement a user authentication system using Mongoose-Auth. Despite following several tutorials and guides on how to use Mongoose-Auth, I have encountered a hurdle. The basic pas ...

Tips for Naming Your GitHub NPM Package for VueJS Package

I decided to customize a git repository of vuejs for my specific project needs. After successfully installing it using the command below: npm install bondythegreat/vue-input-tag --save The installation generates the following line in the package.json fil ...

Tips for developing and exporting an asynchronous function?

When using node and express, I aim to develop a module that utilizes multiple queries. How do I properly export this asynchronous function to app.js? This is the function I am attempting to get working: app.js (where io represents the socketio instance) ...

What is the best approach to effectively handle React and other dependencies in a structured manner?

Context Struggling to configure my AirBnB linting, I made the decision to uninstall all npm packages for a fresh start with my package.json file. Actions Taken I executed the following bash commands: npm uninstall `ls -1 node_modules | tr '/&bsol ...

determining the preference between setTimeout and setImmediate

After reading the documentation on the node, it mentions: setImmediate(callback, [arg], [...]) This function is supposed to execute callback immediately after I/O events callbacks and before setTimeout and setInterval However, in practice, I noticed tha ...

How to effectively execute async MySQL queries in Node.js?

Just starting out with nodejs and struggling to grasp asynchronous programming/callbacks. Here's my current challenge: I need to retrieve all the words from a table in my database whenever a 'post' request is made. I'm trying to achiev ...

What are the best methods for storing configuration settings in my NodeJs CLI application?

Currently, I am developing a nodejs application that functions as a command-line tool. To define my commands, I have opted to use yargs, along with the .config() method to extract default configuration settings from either a .config or config.json file. co ...

Managing a MySQL database in NodeJS using Typescript through a DatabaseController

I'm currently working on a restAPI using nodejs, express, and mysql. My starting point is the app.js file. Within the app.js, I set up the UserController: const router: express.Router = express.Router(); new UserController(router); The UserControll ...

Launch both client and server simultaneously with a single command by utilizing Vue-cli and Webpack

Currently, I am setting up a Vue client with Vue-cli 3 that utilizes Webpack. (To start the client, I run "yarn dev --open") In addition, I am developing a server with an API for the client. (To run the server, I execute "node server/server.js") Is th ...

New issue with installing npm cra

Recently updated to npm version 6.14.4 and encountered an issue while trying to create a new app with cra-template. How can I resolve this problem? npx create-react-app sphinx.ui.react Error Log 50 timing stage:runTopLevelLifecycles Completed in 5234ms ...

Changing the location of an NPM (Gulp) setup to a new directory

After setting up a new project using NPM in the usual way: npm init npm install gulp --save-dev npm install gulp-sass gulp-clean-css gulp-autoprefixer gulp-sourcemaps gulp-uglify gulp-concat --save-dev I then realized, quite clumsily, that I had installe ...

I need to access a directory that is outside of my current folder in Express.js

I have two folders within my main folder; one is for front-end and the other is for back-end. project ├── back-end │ ├── public │ └── routes │ ├── Calling.js │ └── index.js └── front-end ├ ...

Firebase push notification not working in device to device communication

I am developing an Android app and I want authenticated users to be able to send push notifications to each other through a message box. My app is built using node.js with Firebase cloud functions, but I encountered an issue while reviewing the logs: Ty ...

Attempting to use 'user' before it has been initialized is not allowed

I'm encountering a "Cannot access 'user' before initialization" error in my Mern development controller file for a signup function. Controller/user.js const user = require('../models/user') exports.signup = (req,res)=>{ cons ...

How to efficiently send multiple objects in response to a single GET request with Express and Node.js

The code snippet I am working with looks like this - sendServer.get('/download',function(request,response){ var status="SELECT * from poetserver.download where status='0'"; console.log(status) connection.query(status,function(error,ro ...

Learn the simple steps for creating a sub collection within a document using Firebase Function Express

Looking at the code snippet below, you'll see how I tried to create a sub collection of likes under a document with PostID. However, I encountered a 404 error indicating that it was not found. Based on my understanding from the documentation, a collec ...