NodeJS带有链承诺的我的SQL查询

时间:2017-08-23 08:01:01

标签: javascript node.js asynchronous promise q

我有3个功能,我想逐步调用此函数,例如当我将调用firs函数并获得结果时,我必须调用第二个函数并传递从第一个调用返回的参数。在我完成第二次调用后,我必须调用第三个函数并传递从第二个函数返回的参数。

#1:

getCategory = function (branch_id) {
    var deferred = q.defer();
    var categoryData;
    var query = 'SELECT id,name,price,currency FROM category  where branch_id=?';
    pool.getConnection(function (err, connection) {
        connection.query(query, [branch_id], function (error, row, fields) {
            if (error) {
                deferred.reject(error);
            } else {
                connection.release();
                deferred.resolve(row);
            }
        });
    });
    return deferred.promise; }

#2:

getRoom = function (branch_id, categoryID) {
    var deferred = q.defer();
    var roomData;
    var roomSql = 'SELECT id,room_no,name,price,currency FROM room  where branch_id=? and category_id=?';
    pool.getConnection(function (err, connection) {
        connection.query(roomSql, [branch_id, categoryID], function (error, row, fields) {
            if (err) {
                deferred.reject(err);
            } else {
                connection.release();
                deferred.resolve(row);
            }
        });
    });
    return deferred.promise;
}

#3:

getReservationL = function (room_id, start_date, end_date) {
    var deferred = q.defer();
    var reservationData;
    var reservationSql = 'SELECT d.id,d.create_date,d.update_date,d.room_id,d.status_id,d.start_date,d.end_date, ' +
        ' s.name as status_name,a.id as reservation_id,a.person_no as person_no, p.first_name,p.last_name,p.email ' +
        ' FROM reservation_detail d ' +
        ' inner join reservation_status s on d.status_id=s.id ' +
        ' inner join reservation a on d.reservation_id=a.id ' +
        ' inner join person p on a.person_no=p.personal_no ' +
        ' where d.room_id=? and d.start_date >? and d.start_date<?';
    pool.getConnection(function (err, connection) {
        connection.query(reservationSql, [room_id, start_date, end_date], function (error, row, fields) {
            if (err) {
                deferred.reject(err);
            } else {
                connection.release();
                deferred.resolve(row);
            }
        });
    });
    return deferred.promise;
}

我需要这样的东西:

data = getCategory()
for(i=0;i<data.length;i++){
   data[i].room = getRoom(data[i].id);
   for(j=0;j<data[i].room[j].length;j++){
      data[i].room[j].reservation = getReservationL(data[i].room[j].id);
   }
}

如何使用promise或callback在NodeJS中实现此伪代码。我更愿意使用诺言。

更新#1 我在第二次迭代后有这样的数据

[
  {
    "id": 64,
    "name": "VIP",
    "price": 116.5,
    "currency": "USD",
    "room": [
      {
        "id": 44,
        "room_no": "101",
        "name": "A",
        "price": 100,
        "currency": "EUR"
      },
      {
        "id": 274,
        "room_no": "505",
        "name": "a",
        "price": 1,
        "c\r\nurrency": "GEL"
      }
    ]
  },
  {
    "id": 74,
    "name": "SUPER VIP",
    "price": 110,
    "currency": "EUR",
    "room": [
      {
        "id": 54,
        "room_no": "102",
        "name": "A",
        "price": 100,
        "currency": "GEL"
      },
      {
        "id": 284,
        "room_no": "606",
        "name": "a",
        "price": 1,
        "currency": "GEL"
      }
    ]
  },
  {
    "id": 84,
    "name": "DOUBLE",
    "price": 110,
    "currency": "GEL",
    "room": [
      {
        "id": 204,
        "room_no": "103",
        "name": "b",
        "price": 120,
        "currency": "GEL"
      }
    ]
  }
]

我想要对每个房间的每个类别进行迭代。

     getCategory(branch_id).then(firstRecords => {
        let promises = firstRecords.map(function (record) {
            return getRoom(branch_id, record.id)
                .then(roomData => {
                    var res = Object.assign({}, record, { room: roomData });
                    return res;
                })
        });
        return Promise.all(promises);
//HERE i have data that is placed above.
    }).then(secondRecords => {
        let promises = secondRecords.map(function (category) {
            return category.room;
        }).map(function (rooms) {
            console.log('SECOND', rooms);
            return rooms;
        }).map(function (reservation) {
            console.log('THERD', reservation);
            return reservation;
        })
        return Promise.all(promises);
    }).then(reservation => {
        console.log("Reservation", reservation);
    })

更新#2 最终解决方案就在这里

getCategory(branch_id) .then( categories => {

  let roomPromises = categories.map( category => {
    return getRoom(branch_id, category.id)
    .then( rooms => Object.assign({}, category, { rooms }) )   });

  return Promise.all(roomPromises) }) .then( category_rooms => {

  let finalPromise = category_rooms.map( category => {

    let reservationPromises = category.rooms.map( room => {
      return getReservationL(room.id, start_date, end_date)
      .then( reservations => Object.assign({}, room, { reservations }) )
    })

    return Promise.all(reservationPromises)
    .then( room_reservations => {
      return Object.assign({}, category, { rooms: room_reservations })
    });   })

  // const flattenPromise = finalPromise.reduce( (a, b) => a.concat(b), []);   // return Promise.all(flattenPromise);
     return Promise.all(finalPromise) }) .then( data => console.log('final: ', data) )

3 个答案:

答案 0 :(得分:2)

您可以使用.then解决承诺,并可以链接then以同步方式解析多个承诺。

这可能会解决您的使用案例。

getCategory()
.then( firstRecords => {

  console.log('firstRecords: ', firstRecords);

  let promises = firstRecords.map( record => getRoom(record) );
  return Promise.all(promises);
})
.then( secondRecords => {

  console.log('secondRecords: ', secondRecords);

  let promises = secondRecords.map( record => getReservationL(record) );
  return Promise.all(promises);
})
.then( thirdRecords => {

  console.log('thirdRecords: ', thirdRecords);
})

参考: Promise then chaining

  

then方法返回一个允许方法链接的Promise。

     

如果作为处理程序传递的函数然后返回一个Promise,则等效的Promise将在方法链中暴露给后续的

参考: Promise all

  

Promise.all()方法返回一个Promise,它在iterable参数中的所有promise都已解析或者iterable参数不包含promise时解析。它拒绝承认拒绝的第一个承诺。

Promise.all([ { key: 1 }, Promise.resolve(3), 1, true ])
.then( results => {
    results[0]; // { key: 1 }
    results[1]; // 3
    results[2]; // 1
    results[3]; // true
})

更新#1

Promise.all只接受一个promise数组,而不是对象上的promises对象。

# wrong
Promise.all([
    { key: Promise.resolve(1) },
    { key: Promise.resolve(2) },
    { key: Promise.resolve(3) },
])

# right
Promise.all([ 
    Promise.resolve(1), 
    Promise.resolve(2), 
    Promise.resolve(3) 
])

你可以做这样的事情来实现你在评论中提到的内容。

getCategory(branch_id)
.then( firstRecords => {

  console.log('firstRecords: ', firstRecords);

  let promises = firstRecords.map( record => {
    return getRoom(branch_id, record.id)
    .then( roomData => Object.assign({}, record, { room : roomData }) )
  });

  return Promise.all(promises)

})

如果你想追加第一个和第二个诺言的数据,那么解决那里的承诺只能在一个地方访问两者的数据。

更新#2

正如您在评论中提到的,此代码可能会对您有所帮助。

getCategory(branch_id)
.then( categories => {

  let roomPromises = categories.map( category => {
    return getRoom(branch_id, category.id)
    .then( rooms => Object.assign({}, category, { rooms }) )
  });

  return Promise.all(roomPromises)

})
.then( category_rooms => {

  let finalPromise = category_rooms.map( category => {

    let reservationPromises = category.rooms.map( room => {
      return getReservationL(room.id, start_date, end_date)
      .then( reservations => Object.assign({}, room, { reservations }) )
    })

    return Promise.all(reservationPromises)
    .then( room_reservations => {
      return Object.assign({}, category, { rooms: room_reservations })
    });
  })

  return Promise.all(finalPromise)
})
.then( data => console.log(data) )

答案 1 :(得分:0)

由于您的逻辑包含大量用于数据选择和转换的loop语句,因此您需要将其重构为:

getCategory(brandId)
    .then(addRoomData)
    .then(addReservationData)
    .then(handleFinalData)
    .catch(handleError);

对于addRoomDataaddReservationData函数,您可能需要使用Promise.all() combined with .forEach()

如果您想以更易读的方式编写代码,请考虑使用async / await

答案 2 :(得分:-1)

我认为更优化的方法是使用 async 模块的 async.waterfall 功能。 https://caolan.github.io/async

async.waterfall([
function(callback) {
    getSomething(options, function (err, result) {
        if (err) {
            callback(new Error("failed getting something:" + err.message));
            // we should return here
        }
        // since we did not return, this callback still will be called and
        // `processData` will be called twice
        callback(null, result);
    });
},
function(data, callback){
  //do something
}
], function(err,results){
  //get final results
});