Как удерживать приложение NodeJS до тех пор, пока не будет выполнено другое обещание

Используя промисы с NodeJS, я загружаю модель, которую затем можно повторно использовать при последующих вызовах приложения NodeJS. Как я могу предотвратить двойную загрузку одного и того же объекта/модели из базы данных, если второй запрос поступает, пока первый все еще загружается?

Я установил «флаг загрузки», чтобы сказать, что объект извлекается из базы данных и «загружается» по завершении. Если есть второй запрос, который пытается загрузить тот же объект, ему нужно дождаться заполнения исходной модели, после чего оба могут использовать один и тот же объект.

Пример кода (упрощенный, ES6, Node 0.10 [старый по какой-то причине]).

Это TODO, которое нужно решить.

Приложение:

import ClickController from './controllers/ClickController'

import express from 'express'
const app = express()

app.get('/click/*', (req, res) => {

    // Get the parameters here
    let gameRef = "test";

    ClickController.getGameModel(gameRef)
        .then(() => {
            console.log('Got game model')
            return this.handleRequest()
        }, (err) => {
            throw err
        })
}

Контроллер:

import gameModel from '../models/GameModel'

class ClickController {

    constructor(config) {
        // Stores the objects so they can be re-used multiple times.
        this.loadedGames = {}
    }

    // getGameModel() as a promise; return model (or throw error if it doesn't exist)
    getGameModel(gameRef) {
        return new Promise((resolve, reject) => {
            let oGame = false
            if(typeof this.loadedGames[gameRef] === 'undefined') {
                oGame = new gameModel()
                this.loadedGames[gameRef] = oGame
            } else {
                oGame = this.loadedGames[gameRef]
            }

            oGame.load(gameRef)
                .then(function() {
                    resolve()
                }, (err) => {
                    reject(err)
                })
        })
    }
}

Модель/Объект:

class GameModel {

    constructor {
        this.loading = false
        this.loaded = false
    }

    load(gameRef) {
        return new Promise((resolve, reject) => {
            if (this.loading) {

                // TODO: Need to wait until loaded, then resolve or reject

            } else if (!this.loaded) {

                this.loading = true
                this.getActiveDBConnection()
                    .then(() => {
                        return this.loadGame(gameRef)
                    }, (err) => {
                        console.log(err)
                        reject(err)
                    })
                    .then(() => {
                        this.loading = false
                        this.loaded = true
                        resolve()
                    })
            } else {

                // Already loaded, we're fine
                resolve()
            }
        })
    }

    // As this uses promises, another event could jump in and call "load" while this is working
    loadGame(gameRef) {
        return new Promise((resolve, reject) => {

            let sql = `SELECT ... FROM games WHERE gameRef = ${mysql.escape(gameRef)}`

            this.dbConnection.query(sql, (err, results) => {
                if (err) {
                    reject('Error querying db for game by ref')

                } else if (results.length > 0) {

                    // handle results
                    resolve()

                } else {

                    reject('Game Not Found')
                }
            })

        })
    }
}

person Robbie    schedule 14.12.2015    source источник


Ответы (1)


Я не понимаю, какую именно часть вашего кода вы спрашиваете, но обычная схема кэширования значения с обещанием, когда запрос уже находится «в полете», работает следующим образом:

var cachePromise;
function loadStuff(...) {
   if (cachePromise) {
       return cachePromise;
   } else {
       // cache this promise so any other requests while this one is stil
       // in flight will use the same promise
       cachePromise = new Promise(function(resolve, reject) {
          doSomeAsyncOperation(function(err, result) {
              // clear cached promise so subsequent requests
              // will do a new request, now that this one is done
              cachePromise = null;
              if (err) {
                  reject(err);
              } else {
                  resolve(result);
              }
          });
       });
       return cachePromise;
   }
}

// all these will use the same result that is in progress
loadStuff(...).then(function(result) {
   // do something with result
});

loadStuff(...).then(function(result) {
   // do something with result
});

loadStuff(...).then(function(result) {
   // do something with result
});

Это сохраняет кешированное обещание, и, пока запрос находится «в процессе», значение cachePromise остается на месте и будет возвращено последующими запросами.

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

person jfriend00    schedule 14.12.2015
comment
Итак, если я правильно понимаю, cachePromise выполняется только один раз, но может разрешить несколько обработчиков? Имеет смысл: я дам ему вращение в этом направлении. - person Robbie; 14.12.2015
comment
@Robbie - да, cachePromise может иметь много обработчиков .then(), и все они будут возвращать один и тот же фрагмент данных. - person jfriend00; 14.12.2015
comment
Извините за задержку: потребовалось время, чтобы добавить функцию сна :). Работает удовольствие; именно то, что нужно. Спасибо за ясное объяснение. - person Robbie; 14.12.2015