Pet*_*erT 4 javascript node.js promise
我一直在学习 Node (7.4.0) 中的 ES6 Promise,因为我想应用它们来处理串行通信。我做出了一个承诺,它是许多较小承诺的集合,允许我使用发送者、事件监听器和超时来序列化与设备的通信。但是,我不太理解 .then() 链接,因为我需要添加一些额外的闭包,这些闭包看起来与我在许多示例中看到的不同,这让我相信我误解了一些基本的东西。考虑这个函数(我删除了所有的prototype/this.code以使其更小):
function sendAck(command, ack, timeout) {
return new Promise((resolve, reject) => {
if (gReady === undefined) reject('not ready');
// each of these three functions returns a promise
let sendPromise = createSend(command);
let ackPromise = createAck(ack);
let timeoutPromise = createTimeout(timeout);
// p1 = we hear a response, or timeout waiting for one
let p1 = Promise.race([ackPromise, timeoutPromise]);
// both p1 -and- send function need to resolve
let p2 = Promise.all([p1, sendPromise]);
p2.then(values => resolve(values)).catch(err => {
localCleanup(); /// otherwise just return p2, but need to do this
reject(err)
});
}
}
Run Code Online (Sandbox Code Playgroud)
现在,当我尝试将一堆 sendAck() 与 then 链接起来时,我发现这种用法失败了,因为它们都同时执行:
sendAck('init', 'pass', 3000)
.then(sendAck('enable a', 'pass', 3000))
.then(sendAck('enable b', 'pass', 3000))
:
Run Code Online (Sandbox Code Playgroud)
所以我必须将每个都包装在一个闭包中才能使其工作,因为闭包是在 then() 上计算的,而不是由 JS 解释器计算的函数。这感觉就像我错过了一些非常重要的东西,因为它看起来很尴尬:
sendAck('init', 'pass', 3000)
.then(() => { return sendAck('enable a', 'pass', 3000) })
.then(() => { return sendAck('enable b', 'pass', 3000) })
:
Run Code Online (Sandbox Code Playgroud)
我很困惑,因为我在网上看到其他示例,其中 .then() 包含一个返回承诺的函数,例如......
.then(onHttpRequest)
Run Code Online (Sandbox Code Playgroud)
这显然不同于
.then(onHttpRequest())
Run Code Online (Sandbox Code Playgroud)
必须将 .then() 与闭包链接起来似乎很奇怪。我这样做是否正确,只是不习惯,还是我错过了什么?
提前致谢。
PT
编辑:如下所述,我的问题中没有闭包,只有匿名函数。
到目前为止,我在您的示例中没有看到任何结束。是的,我知道一些编程语言将匿名函数称为“闭包”,但以我的愚见,这些语言的开发人员只是对闭包是什么有误解。当然,在 javascript 中,我们不将匿名函数称为闭包,我们将其称为“匿名函数”。
首先,忘记承诺。假设您有这段代码:
function a (x) {return x*2}
var b = a(5);
Run Code Online (Sandbox Code Playgroud)
现在,在任何编程语言中,无论是 Java、C++ 还是 javascript,您期望 的值是多少b?你期望b是这样10还是你期望它是这样function(){return 10}?执行上面的代码后,您是否希望能够执行以下操作:
console.log(b);
Run Code Online (Sandbox Code Playgroud)
或者您认为您必须这样做:
console.log(b());
Run Code Online (Sandbox Code Playgroud)
显然你会说bis 10,而不是返回的函数10。所有语言都是这样工作的,对吧?让我们把这个例子变得更复杂一些:
function a (x) {return x*2}
console.log(a(5));
Run Code Online (Sandbox Code Playgroud)
在上面的代码中,您希望console.log()打印function a(x){..}还是希望它打印10?显然它会打印 10。因为我们知道在编程语言中,当我们调用函数时,调用的结果不是函数本身,而是函数的返回值。请注意,上面的代码与以下代码完全相同:
function a (x) {return x*2}
var y = a(5);
console.log(y);
Run Code Online (Sandbox Code Playgroud)
如果我们想打印这个函数,我们会这样做:
console.log(a);
Run Code Online (Sandbox Code Playgroud)
在可以像传递数字或字符串一样传递函数的世界中,您需要更加了解函数和函数调用之间的区别。上面的代码中,a是一个函数。您可以像传递任何其他对象一样传递它。a()是一个函数调用,该函数调用的结果是该函数的返回值。
因此,在您的代码中,当您执行以下操作时:
sendAck('init', 'pass', 3000)
.then(sendAck('enable a', 'pass', 3000))
.then(sendAck('enable b', 'pass', 3000));
Run Code Online (Sandbox Code Playgroud)
这与做相同
// Functions below are async, they return immediately without waiting
// for data to be returned but returns promises that can wait for
// data in the future:
var a = sendAck('init', 'pass', 3000);
var b = sendAck('enable a', 'pass', 3000);
var c = sendAck('enable b', 'pass', 3000);
// Now we wait for return data:
a.then(b).then(c);
Run Code Online (Sandbox Code Playgroud)
请注意,虽然按.then()顺序解析,但它们sendAck是并行发送的,因为我们在调用下一个之前不会等待一个解析返回数据。
sendAck正如您所发现的,解决方案是在获得数据之前不要调用。所以我们需要这样做:
// We're not calling `sendAck` here, we're just declaring functions
// so nothing gets sent:
function a () {return sendAck('init', 'pass', 3000)}
function b () {return sendAck('enable a', 'pass', 3000)}
function c () {return sendAck('enable b', 'pass', 3000)}
// Now we can fire them in sequence:
a().then(b).then(c);
Run Code Online (Sandbox Code Playgroud)
请注意,我们通过调用来让事情滚动a(),但我们实际上并没有调用b或c- 我们让它then为我们做。因为我们传递函数本身而不是调用它们,所以它们的函数体不会被执行。
当然,我们不需要创建命名函数。正如您自己发现的那样,我们可以轻松地使用匿名函数来实现此目的。所以上式可以改写为:
sendAck('init', 'pass', 3000)
.then(() => { return sendAck('enable a', 'pass', 3000) })
.then(() => { return sendAck('enable b', 'pass', 3000) });
Run Code Online (Sandbox Code Playgroud)