我一直在学习Node(7.4.0)中的ES6承诺,因为我想应用它们来处理串行通信。我做了一个承诺,它是许多较小的承诺的集合,允许我使用发送者,EventListener和超时来序列化与设备的通信。但是,我不太明白.then()链接,因为我需要添加一些与我在许多示例中看到的不同的其他闭包,这使我相信我误解了一些基本的东西。考虑一下这个函数(我删除了所有原型/这个。代码以使其更小):
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)
});
}
}
现在,当我尝试将一堆sendAck()链接起来时,我发现这个用法失败了,因为它们都是立即执行的:
sendAck('init', 'pass', 3000)
.then(sendAck('enable a', 'pass', 3000))
.then(sendAck('enable b', 'pass', 3000))
:
所以我必须将每个包装在一个闭包中以使其工作,因为闭包是在then()而不是由JS解释器评估的函数上计算的。感觉我错过了一些非常重要的东西,因为它看起来很尴尬:
sendAck('init', 'pass', 3000)
.then(() => { return sendAck('enable a', 'pass', 3000) })
.then(() => { return sendAck('enable b', 'pass', 3000) })
:
我感到困惑,因为我在网上看到其他例子.then()包含一个返回承诺的函数,比如...
.then(onHttpRequest)
明显不同于
.then(onHttpRequest())
使用闭包链接.then()似乎很奇怪。我这样做是否正确而且不习惯,或者我错过了什么?
提前致谢。
PT
编辑:如下所述,我的问题中没有闭包,只是匿名函数。
答案 0 :(得分:2)
.then(sendAck('enable a', 'pass', 3000))
同步执行sendAck
函数,启动该承诺,并将结果传递给.then()
,.then()
将尝试稍后调用该结果当承诺链继续,但你已经调用它,所以它不会等待异步承诺链。
您也可以将其缩短为
.then(() => sendAck('enable a', 'pass', 3000))
或者你可以传入一个绑定函数引用,promise将在稍后调用:
.then(sendAck.bind(null, 'enable a', 'pass', 3000))
答案 1 :(得分:1)
当您将函数传递给.then(..)
方法时,实际上是在告诉js评估(或调用)函数,因为您已经使用了括号并添加了参数:
sendAck('enable a', 'pass', 3000); // runs immediately, then passes value to .then(..)
在尝试将任何内容传递到.then(..)
如果您不想使用箭头功能,通常会写一些类似
的内容.then(function () {
sendAck('enable a', 'pass', 3000);
});
实际上是相同的,但与箭头函数
的范围不同答案 2 :(得分:1)
如果您将sendAck函数变为高阶函数,那么您可以像原来一样使用.then
:
const 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)
});
};
};
sendAck('init', 'pass', 3000)()
.then(sendAck('enable a', 'pass', 3000))
.then(sendAck('enable b', 'pass', 3000));
或者,您可以使用Andy Ray's answer中较短的语法。我不认为你的方法/用法存在任何问题。
你也可以缩短
p2.then(values => resolve(values)).catch(err => {
localCleanup(); /// otherwise just return p2, but need to do this
reject(err)
});
为:
p2.then(resolve).catch(err => {
localCleanup();
reject(err);
});
答案 3 :(得分:1)
到目前为止,我没有看到你的例子中有任何关闭。是的,我知道一些编程语言称为匿名函数"闭包"但在我看来,这些语言的开发人员只是对闭包的误解。当然在javascript中我们不会调用匿名函数闭包,我们称之为#34;匿名函数"。
首先,忘掉承诺。让我们说你有这段代码:
function a (x) {return x*2}
var b = a(5);
现在,在任何编程语言中,无论是Java还是C ++或javascript,您期望b
的价值是什么?您希望b
是10
还是期望function(){return 10}
?执行上面的代码后,您希望能够执行此操作:
console.log(b);
或者你认为你必须这样做:
console.log(b());
显然,您说b
是10
,而不是返回10
的函数。所有语言都是这样的吗?所以让我们让这个例子更复杂一点:
function a (x) {return x*2}
console.log(a(5));
在上面的代码中,您希望console.log()
打印function a(x){..}
还是打算打印10
?显然它会打印10.因为我们在编程语言中知道当我们调用一个函数时,该调用的结果不是函数本身而是函数的返回值。请注意,上面的代码完全相同:
function a (x) {return x*2}
var y = a(5);
console.log(y);
如果我们想要打印我们要执行的功能:
console.log(a);
在一个可以传递函数的世界中,你可以通过数字或字符串传递函数,你需要更加了解函数和函数调用之间的区别。在上面的代码中,a
是一个函数。你可以传递它,就像你传递任何其他对象一样。 a()
是一个函数调用,该函数调用的结果是函数的返回值。
因此,在您的代码中,当您这样做时:
sendAck('init', 'pass', 3000)
.then(sendAck('enable a', 'pass', 3000))
.then(sendAck('enable b', 'pass', 3000));
与做
相同// 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);
请注意,虽然.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);
请注意,我们通过致电a()
来推动这项活动,但我们实际上并没有致电b
或c
- 我们让then
为我们做了。因为我们自己传递功能而不是称他们为自己的身体而不会被执行。
当然,我们不需要创建命名函数。正如您自己发现的那样,我们可以轻松地使用匿名功能。所以上面的内容可以改写为:
sendAck('init', 'pass', 3000)
.then(() => { return sendAck('enable a', 'pass', 3000) })
.then(() => { return sendAck('enable b', 'pass', 3000) });