问题
I have been struggling with Promises and would like to know how things work with Promises. In my project, I am using Bookshelfjs ORM for fetching the data from Postgres.
Here is the code that I am working on right now. I get an array of device ids in this request and each device is running in one of the two modes.
router.post('/devices', function (req, res, next) {
var currentData = [];
var deviceIds = req.body.devices;
loadash.forEach(deviceIds, function (device) {
var deviceid = device.deviceid;
Device.forge()
.where({deviceid: deviceid})
.fetch({columns: ['id', 'mode']})
.then(function (fetchedDevice) {
if(fetchedDevice.get('mode') === 1) {
Model_1.forge()
.where({device_id: fetchedDevice.get('id')})
.orderBy('epoch_time', 'DESC')
.fetch()
.then(function (modelOne) {
//first push
currentData.push(modelOne.toJSON());
//array with first push data
console.log(currentData)
})
.catch(function (err) {
console.log(err);
});
}
else if(fetchedDevice.get('mode') === 2) {
Model_2.forge()
.where({device_id: fetchedDevice.get('id')})
.orderBy('epoch_time', 'DESC')
.fetch()
.then(function (modelTwo) {
//second push
currentData.push(modelTwo.toJSON());
//array not empty here(shows data from both push)
console.log(currentData);
})
.catch(function (err) {
console.log(err);
});
}
})
.catch(function (err) {
console.log(err);
});
});
//This shows an empty array
console.log('Final: ' +currentData);
});
Now, I know this is happening because of async nature of Javascript. My question is
how can I display final array after all
push()
have been executed? I tried doing this usingPromise.all()
method but did not have any success.is it possible to return
modelOne
ormodelTwo
out of every promise and then push to an array? How can I achieve this?
回答1:
Use .map()
and Promise.all()
, return
a value from function passed to .then()
var currentData = loadash.map(deviceIds, function (device) {
var deviceid = device.deviceid;
return Device.forge()
.where({deviceid: deviceid})
.fetch({columns: ['id', 'mode']})
.then(function (fetchedDevice) {
if(fetchedDevice.get('mode') === 1) {
// return value from `.then()`
return Model_1.forge()
.where({device_id: fetchedDevice.get('id')})
.orderBy('epoch_time', 'DESC')
.fetch()
.then(function (modelOne) {
// return value from `.then()`
return modelOne.toJSON();
})
.catch(function (err) {
console.log(err);
});
}
else if(fetchedDevice.get('mode') === 2) {
// return value from `.then()`
return Model_2.forge()
.where({device_id: fetchedDevice.get('id')})
.orderBy('epoch_time', 'DESC')
.fetch()
.then(function (modelTwo) {
// return value from `.then()`
return modelTwo.toJSON();
})
}
})
});
var res = Promise.all(currentData);
res
.then(function(results) {console.log(results)})
.catch(function (err) {
console.log(err);
});
回答2:
Try to avoid nesting then
, and keep the promise chain flat. Furthermore, you can join the two model cases into one piece of code (DRY). Finally, use map
instead of forEach
so you return an array of promises, which you can then feed to Promise.all
:
router.post('/devices', function (req, res, next) {
var promises = loadash.map(req.body.devices, function (device) {
return Device.forge()
.where({deviceid: device.deviceid})
.fetch({columns: ['id', 'mode']})
.then(function (fetchedDevice) {
var model = [Model_1, Model_2][fetchedDevice.get('mode')-1];
if (model) {
return model.forge()
.where({device_id: fetchedDevice.get('id')})
.orderBy('epoch_time', 'DESC')
.fetch();
}
}).catch(function (err) {
console.log(err);
});
});
Promise.all(promises).then(function (currentData) {
currentData = currentData.filter(model => model) // exclude undefined
.map(model => model.toJSON());
console.log('Final: ' +currentData);
});
}
来源:https://stackoverflow.com/questions/45122086/how-to-return-value-from-a-promise