我正在编写一个Node.js应用程序:
此Node.js代码用于从HTTP" POST"中读取文件数据。消息完美无缺:
// 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}));
});
问题在于,读取文件数据只是我需要做的几件事的第一,所有这些都涉及异步回调。所以我尝试使用promises来序列化调用(根据需要优雅地处理错误):
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();
}
问题:
第一个函数parsePostMsg()
本身有一个回调函数。永远不会被调用:
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!
});
});
}
问:我如何正确1)创建承诺,然后2)调用parsePostMsg()
以便正确地沿着链调用3)form.parse()
?
问:resolve()
和reject()
怎么样?他们在哪里适合?
=============================================== =======================
ADDENDUM:我尝试了很多东西,到目前为止没有任何工作。
目标:让这些功能(及其相关的回调,如果适用)按此顺序运行:
这是一个失败的例子:
/*
* 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);
答案 0 :(得分:3)
您失败的示例不起作用,因为您没有正确链接。
让我们看一下这段代码:
parsePostMsg(req, data)
.then(sendAck(res, data))
.then(writeTempFile(data))
.then(deleteTempFile(data))
// This console.log will execute way before
// the promise is resolved
console.log("Done: data=", data);
发生的事情是正确调用parsePostMsg(),但之后的所有内容都将在promise解析之前执行。这是因为您实际上是在立即执行这些函数,然后这些执行的输出就是promise在解析时将尝试使用的内容。这就是为什么,如果你将parsePostMsg()中的超时设置为几秒钟,那么该函数的输出将被记录到最后。
所以这应该是这样的:
parsePostMsg(req, data)
.then(sendAck)
.then(writeTempFile)
.then(deleteTempFile)
.then(function () {
// Now the console.log will log when everything is done.
console.log("Done: data=", data);
});
在这里,你告诉它承诺在promise解决时应该执行这些函数。但要做到这一点,我们必须以正确的方式建立承诺链。要将这些方法链接在一起,我们必须在前一个函数中返回我们想要在函数中使用的值。例如,我们必须在parsePostMsg()函数中返回sendAck()函数的参数。
让我们编写parsePostMsg,以便它返回链中所需的所有参数。
function parsePostMsg(req, res, 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);
// Here we pass all the arguments into the resolve method
// This means that the following then() call will receive
// These argument. Take note that this is an array.
resolve([req, res, data]);
}, 3000);
});
}
现在我们已经更改了parsePostMsg,以便它将在链中传递它的所有参数。让我们以同样的方式改变其他方法。
function sendAck(req, res, data) {
console.log("sendAck...");
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log("sendAck@acknowledging", res, data);
resolve([req, res, data]);
}, 3000);
});
}
function writeTempFile(req, res, data) {
console.log("writeTemp...");
return new Promise(function (resolve, reject) {
setTimeout(function () {
data.temp = "foo";
console.log("writeTemp@setting data.temp", data);
resolve([req, res, data]);
}, 3000);
});
}
function deleteTempFile(req, res, 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([req, res, data]);
}, 3000);
});
}
现在请注意,所有函数都接受3个参数,但我们使用数组调用resolve方法。如果我们只是简单地使用.then(),那么它将无法正常工作,但bluebird提供了一个名为.spread()的特殊辅助方法,它解析数组并使用数组的所有成员调用该方法。请注意,如果您使用的是ES2015,则可以使用ES2015销毁而不是使用.spread(
因此,使用.spread()代码应如下所示:
parsePostMsg(req, res, data)
.spread(sendAck)
.spread(writeTempFile)
.spread(deleteTempFile)
.spread(function (req, res, data) {
// Now the console.log will log when everything is done.
console.log("Done: data=", data);
});
这是您提供正确工作的失败示例:
var Promise = require('bluebird');
var req = {},
res = {},
data = {};
var temp;
function parsePostMsg(req, res, 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([req, res, data]);
}, 3000);
});
}
function sendAck(req, res, data) {
console.log("sendAck...");
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log("sendAck@acknowledging", res, data);
resolve([req, res, data]);
}, 3000);
});
}
function writeTempFile(req, res, data) {
console.log("writeTemp...");
return new Promise(function (resolve, reject) {
setTimeout(function () {
data.temp = "foo";
console.log("writeTemp@setting data.temp", data);
resolve([req, res, data]);
}, 3000);
});
}
function deleteTempFile(req, res, 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([req, res, data]);
}, 3000);
});
}
console.log("Invoking parsePostMsg...");
parsePostMsg(req, res, data)
.spread(sendAck)
.spread(writeTempFile)
.spread(deleteTempFile)
.spread(function (req, res, data) {
console.log("Done: data=", data);
})
.catch(function(err) {
// Always put a .catch() at the end of your promise chains, ALWAYS,
// it is literally the ultimate method to handle promise errors.
console.warn(err);
});
答案 1 :(得分:2)
在此之前,我想提一下Bluebird有一个名为promisify
的函数,它将基于回调的函数转换为promise-returns函数。
但是在引擎盖下......
创建承诺
你已经走上正轨了。承诺是具有2个状态(已解决和拒绝)的对象。您需要做的就是在解析或拒绝时定义。这就是resolve
和reject
函数的用途。
function parsePostMsg(req, data) {
// Return a promise
return new Promise(function(resolve, reject) {
var form = new multiparty.Form();
// that callse your async, callback-ish function during construction
form.parse(req, function(err, fields, files) {
// that rejects when there's an error
if(err) reject(err);
// or resolves when everything goes well
else resolve({ fields: fields, files: files });
});
});
}
调用parsePostMsg()
要知道你的promisified异步操作何时解析或拒绝,promises会公开一个接受2个回调的then
方法。第一个在他们承诺解决时执行,第二个在承诺被拒绝时执行。它们都是在调用resolve
/ reject
时使用的参数。
在这种情况下,当promise被拒绝时,我们期望来自form.parse
的错误,或者当promise被解析时,我们期望包含字段和文件的对象。
// parsePostMsg returns a promise where we hook a then
parsePostMsg(req, data).then(function(data){
// The object you passed to `resolve` is `data` in here
// data.fields
// data.files
}, function(error){
// The `err` you passed to `reject` is `error` in here
})
我是否正确地在正确的地方创造了“承诺”????
参见第一栏
resolve()和reject()怎么样?他们在哪里适合?
参见第一栏
答案 2 :(得分:2)
我对承诺不熟悉,但你应该读一下,https://promisesaplus.com/,我认为你的尝试捕获也没用,因为承诺抛出的任何异常都与该代码分开。
resolve函数是在履行承诺时运行的函数,拒绝是在失败时运行,拒绝是您可能希望进行错误处理的地方。请记住,附在承诺上的所有内容都与周围的代码无关。
答案 3 :(得分:0)
你真的非常接近!您正在正确创建承诺并使用.then
调用它。不幸的是,你链条中的下一步永远不会开始,因为你的承诺永远不会解决!
resolve
和reject
是承诺的说法&#34;这有效,继续下一步&#34;或者&#34;这没有用,抛出一个错误!&#34;分别。在你的情况下,你可能想要做这样的事情:
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) {
if (err) {
reject(err)
} else {
data.fields = fields;
data.files = files;
resolve();
}
});
});
}
请注意,使用承诺模型,您可以&#34;返回&#34;在内部方法中使用外部方法中的resolve
!
请注意,您需要确保链中的所有步骤都遵循此模式。我使用setTimeout
模拟异步函数调用,将一个简单的示例放在一起,以显示它的样子:
var data = {};
var req, res;
var temp;
function parsePostMsg(req, data) {
console.log("parsePostMsg...");
return new Promise(function(resolve, reject) {
setTimeout(function() {
data.fields = [];
data.files = [];
resolve();
}, 10);
});
}
function sendAck(req, data) {
console.log("sendAck...");
return new Promise(function(resolve, reject) {
setTimeout(function() {
console.log("acknowleding", req, data);
resolve();
}, 10);
});
}
function writeTempFile() {
console.log("writeTemp...");
return new Promise(function(resolve, reject) {
setTimeout(function() {
temp = "foo";
resolve();
}, 10);
});
}
function deleteTempFile() {
console.log("deleteTemp...");
return new Promise(function(resolve, reject) {
setTimeout(function() {
console.log("temp was ", temp);
temp = undefined;
resolve();
}, 10);
});
}
parsePostMsg(req, data)
.then(sendAck(res, data))
.then(writeTempFile())
.then(deleteTempFile());
&#13;