I'm writing a Node.js app that:
- accepts a file (from an HTTP "POST"), then
- writes the file to a Box.com repository.
This Node.js code to read the file data from the HTTP "POST" message works perfectly:
// ORIGINAL (non-Promise; parse only)
app.post('/upload', function(req,res) {
console.log('/upload...');
var form = new multiparty.Form ();
form.parse(req, function(err, fields, files) {
res.writeHead(200, {'content-type': 'text/plain'});
res.write('received upload:\n\n');
res.end(util.inspect({fields: fields, files: files}));
});
The problem is that reading the file data is just the first of several things I need to do, all of which involve asynchronous callbacks. So I'm trying to use promises to serialize the calls (gracefully handling errors as needed):
var Promise = require('bluebird');
...
// IDEAL SOLUTION (using "promises")
var data = {};
try {
parsePostMsg(req, data))
.then(sendAck(res, data))
.then(writeTempFile())
.then(sendToBox())
.then(deleteTempFile());
} catch (e) {
console.log("app.post(/upload) ERROR", e);
deleteTempFile();
}
PROBLEM:
The very first function, parsePostMsg()
, itself has a callback. Which never gets invoked:
function parsePostMsg(req, data) {
console.log("parsePostMsg...");
return new Promise(function(resolve, reject) {
var form = new multiparty.Form ();
form.parse(req, function(err, fields, files) {
data.fields = fields; // <-- This never gets called, so fields & files
data.files = files; // never get initialized!
});
});
}
Q: How do I correctly 1) Create the promise, then 2) invoke parsePostMsg()
so that 3) form.parse()
correctly gets invoked along the chain?
Q: Am I even creating the "Promise" correctly, in the right place????
Q: What about resolve()
and reject()
? Where do they fit in?
======================================================================
ADDENDUM: I've tried many things, nothing has worked so far.
GOAL: to get these functions (and their associated callbacks, where applicable) to run in this order:
- parsePostMsg(req, data) // Wait for "parse form data" callback to complete
- sendAck(res, data) // Synchronous
- writeTempFile(data) // Build an HTTP message, send it, and wait for response from the remote server
- deleteTempFile(data) // Cleanup when everything is done
Here's a failing example:
/*
* If the "timeout" values are the same (e.g. "10"), everything works fine.
*
* But if the timeout values are *different*, the order gets scrambled:
* Invoking parsePostMsg...
* ... OK ...
* Done: data= {}
* writeTemp@setting data.temp { temp: 'foo' }
* sendAck@acknowledging {} { temp: 'foo' }
* deleteTempFile@callback: data.temp was foo
* parsePostMsg@setting data.{fields, files} {} { temp: undefined, fields: [], files: [] }
*/
var Promise = require('bluebird');
var req = {}, res = {}, data = {};
var temp;
function parsePostMsg(req, data) {
console.log("parsePostMsg...");
return new Promise(function(resolve, reject) {
setTimeout(function() {
data.fields = [];
data.files = [];
console.log("parsePostMsg@setting data.{fields, files}", req, data);
resolve();
}, 35);
});
}
function sendAck(req, data) {
console.log("sendAck...");
return new Promise(function(resolve, reject) {
setTimeout(function() {
console.log("sendAck@acknowledging", req, data);
resolve();
}, 5);
});
}
function writeTempFile(data) {
console.log("writeTemp...");
return new Promise(function(resolve, reject) {
setTimeout(function() {
data.temp = "foo";
console.log("writeTemp@setting data.temp", data);
resolve();
}, 2);
});
}
function deleteTempFile(data) {
console.log("deleteTemp...");
return new Promise(function(resolve, reject) {
setTimeout(function() {
console.log("deleteTempFile@callback: data.temp was ", data.temp);
data.temp = undefined;
resolve();
}, 15);
});
}
console.log("Invoking parsePostMsg...");
parsePostMsg(req, data)
.then(sendAck(res, data))
.then(writeTempFile(data))
.then(deleteTempFile(data));
console.log("Done: data=", data);