Node.js несколько обратных вызовов / последовательное выполнение обратного вызова

Итак, я пытаюсь создать приложение node.js-mongoose, которое предоставляет определенные конечные точки RESTful API. Вот высокоуровневая структура:

index.js
|
models
|-- user.js
|-- keys.js
controllers
|-- user.controller.js
|-- keys.controller.js
routes
|-- user.routes.js
|-- keys.routes.js

Вот образец, которому я следую для каждой отдельной детали:

myModel.js (файл модели)

import mongoose from 'mongoose';
const Schema = mongoose.Schema;
const mySchema = new Schema({
  id: { type: 'String', required: true, unique: true },
});
let MyModel = mongoose.model('MyModel', mySchema);
export default MyModel;

my.controller.js (файл контроллера)

import MyModel from '../models/myModel';
const MyController = {};

// Add a new document
MyController.addNewDocument = async (req, res) => {
  try {
    // validate req

    // save to database
    await newDocument.save((err, saved) => {
      if (err) {
        console.info(err);
        return res.status(500).send(err);
      } else {
        return res.status(201).json({ document: saved });
      }
    });
  } catch (err) {
    console.error('Unexpected error in addNewDocument method');
    return res.status(500).send(err);
  }
};

export default MyController;

my.routes.js (файл роутера)

import { Router } from 'express';
import MyController from '../controllers/my.controller';
const router = new Router();

// Add a new document
router.post('/documents', (req, res) => {
  MyController.addNewDocument(req, res);
});

export default router;

Итак, у меня есть эти операции CRUD (контроллеры и маршруты) как для users, так и для keys. Требуемое поведение состоит в том, что каждый раз, когда я создаю нового пользователя, должен создаваться новый набор ключей с бизнес-логикой в ​​функции, которая предоставляется файлом контроллера для ключа.

Вопрос: Как выполнить их по очереди? Какой должна быть правильная выкройка?

Вещи, которые я пробовал:

  1. Обратный вызов ад
  2. вызов других конечных точек из функции контроллера с помощью res.redirect и т. д.

Вещи, которые я исследую:

  1. Обещания
  2. Множественные обратные вызовы

Но я все еще не уверен, каким образом можно было бы добиться такого поведения.

Что ожидает ваша асинхронная функция addNewDocument в my.controller.js? И если addNewDocument предназначен для ожидания, то почему вы не ожидаете этого в my.routes.js?

Sivcan Singh 17.07.2018 08:01

@SivcanSingh - Конечно. функция addNewDocument выполняет операции с базой данных (обновлен комментарий '// save'). Я помещаю код, чтобы показать образец, которому я следую, с маршрутами, моделями и контроллерами. Не следует принимать за точный код. И мой вопрос, я думаю, совсем по другой теме.

mathakoot 17.07.2018 08:14

Почему вы используете функцию async без ключевого слова await?

Bergi 17.07.2018 10:28

@mathakoot Сообщество поможет вам только в том случае, если они прочитали правильный код. :) Я был здесь только для этого. Даже если вы выполняете операцию db, вам нужно «дождаться» этой операции где-то в самой функции. Больше нет смысла писать async.

Sivcan Singh 17.07.2018 12:52

@SivcanSingh - Я хотел сказать, что я не пытаюсь отлаживать свой код. Я просто хотел показать образец, которому я следую с функциями контроллера и маршрутизатора. Но если это поможет, позвольте мне обновить его.

mathakoot 17.07.2018 23:14

@Bergi - добавлено.

mathakoot 17.07.2018 23:15

@mathakoot Я не думаю, что newDocument.save() возвращает обещание, когда вы передаете обратный вызов

Bergi 18.07.2018 09:25
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
2
7
140
4

Ответы 4

Шаблон, который я использую для очередей, использует обещания.

const promiseQueue = Promise.resolve();

function patchFile(request, response) {
 promiseQueue
  .then(() => updateFile(request))
  .then(v => respondValid(v, response))
  .catch(e => {
    console.error(e);
    respondError("Error patching resource", response);
  });
}

У Mongoose есть хуки до и после сохранения. документы здесь

Вы можете использовать их, чтобы избежать обратных вызовов и сохранить отдельную логику.

import mongoose from 'mongoose';
const Schema = mongoose.Schema;

const keySchema = new Schema({
   user: { type: Schema.Types.ObjectId, ref: 'User' },
   key: { type: 'String', required: true, unique: true },
});

const userSchema = new Schema({
    _id: Schema.Types.ObjectId,
    name: String,
});

userSchema.post('save', (newUser) => {
    // generate key
    new Key({
        user: newUser._id,
        key: '1234567890'
    }).save();

});


let User = mongoose.model('User', userSchema);
let Key = mongoose.model('Key', keySchema);

Лучший способ избежать ада обратных вызовов - использовать несколько методов, предлагаемых async, вместе с обещаниями.

async.waterfall([
  function(callback){
    .....
    callback(null, res)
  }
 ],
 function(error, result){
 .....
})

https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/Promise.jsm/Deferred

Ссылка выше показывает самый простой способ использования обещаний.

Возможно, мне что-то не хватает, но мне кажется, что вы одновременно используете обратный вызов и await. Почему бы просто не await и нормально справиться со всем?

// Add a new document
router.post('/documents', async (req, res) => {
  let doc = await MyController.addNewDocument(req.body);
  res.json(doc);
});

...

MyController.addNewDocument = (doc) => {

  // Validate doc.
  ...

  // Save doc (this returns a promise).
  return MyModel.create(doc);

};

Мне не хватает попытки / уловки в функции async (используйте свое воображение!). Еще одна вещь, на которую стоит обратить внимание, и это только мое мнение - вы можете в конечном итоге пожалеть о передаче полных объектов req и res вашим контроллерам. Посмотрите, как я сдал только req.body. Это гарантирует, что у контроллера есть только обязанности, связанные с документом (или какой бы то ни было работой), и он ничего не знает о транзакции HTTP и других вещах, которые ему не нужны.

Другие вопросы по теме