为 Promise 添加监听器后,我应该使用原来的 promise 还是新的?
after adding a listener to a Promise, should I use the original promise or the new one?
我有一些采用现有承诺的 javasript 代码
(例如,承诺 return 由 fetch() 编辑)并增加价值
(比如,then/catch 个用于调试的侦听器,或者更多):
let myFetch = function(url) {
return fetch(url).then(function(value) {
console.log("fetch succeeded: value=",value);
return value;
}.catch(function(reason) {
console.log("fetch failed: reason=",reason);
throw reason;
});
};
我发现自己修改了上面的代码,以便只有在某些条件为真时才添加侦听器:
let myFetch = function(url) {
let promise = fetch(url);
if (some condition) {
promise = promise.then(function(value) {
console.log("fetch succeeded: value=",value);
return value;
}.catch(function(reason) {
console.log("fetch failed: reason=",reason);
throw reason;
});
}
return promise;
};
现在我想知道,return 由 "then" 编辑的新承诺 return 对 myFetch 真的有意义吗
(实际上是 shorthand 另一个 "then")如上所述,
还是 return 最初的承诺(增加听众)更有意义?
换句话说,我正在考虑省略第二个 "promise =",
这样代码看起来像这样:
let myFetch = function(url) {
let promise = fetch(url);
if (some condition) {
promise.then(function(value) {
console.log("fetch succeeded: value=",value);
return value;
}.catch(function(reason) {
console.log("fetch failed: reason=",reason);
throw reason;
});
}
return promise;
};
这与以前的版本有明显不同吗?
哪个版本更可取,如果是,为什么?
好吧,如果您的成功处理程序 return
是值而您的拒绝处理程序 throw
是错误 - 那么它基本上就是承诺的身份转换。
您不仅不需要这样做 promise = promise.then
您甚至不需要 return 值:
let myFetch = function(url) {
let promise = fetch(url);
if (some condition) {
promise.then(function(value) {
console.log("fetch succeeded: value=",value);
}.catch(function(reason) {
console.log("fetch failed: reason=",reason);
});
}
return promise;
};
也就是说,如果您使用的是 ES6 和 let,您可以使用箭头函数,这无论如何都会使它变得更好:
let myFetch = function(url) {
let promise = fetch(url);
if (some condition) {
promise.then(value => console.log("fetch succeeded: value=",value))
.catch(reason => console.log("fetch failed: reason=",reason));
}
return promise;
};
像 bluebird 这样的一些 promise 库为此提供了一个 tap 实用程序。唯一的问题是,如果 fetch
添加了对 promise 取消的支持,那么如果你不链接它,你就会用 if (some condition)
处理程序打破链。
如果您的唯一用例是在 then
/catch
中记录某些内容 – 只要一切顺利,这应该无关紧要。如果出现异常,事情会变得更加混乱。考虑这两个例子:
Return原承诺
function myFetch() {
let promise = new Promise(function (resolve, reject) {
resolve(100);
});
promise.then(function () { throw new Error('x'); });
return promise;
}
myFetch().then(function () {
console.log('success!');
}).catch(function (e) {
console.error('error!', e)
});
结果是 success
并且在内部 then
中抛出的错误可能会在一些 promise 库中被吞没(尽管最流行的库如 Bluebird 处理这个并且你会得到额外的错误 Unhandled rejection Error: x
).
使用 native Promises in some environments.
时,错误也可能被吞没
Return正在修改承诺
function myFetch() {
let promise = new Promise(function (resolve, reject) {
resolve(100);
});
promise = promise.then(function () { throw new Error('x'); });
return promise;
}
myFetch().then(function () {
console.log('success!');
}).catch(function (e) {
console.error('error!', e)
});
现在结果是error! Error: x
。
你承诺分支。在第二种情况下,您实际上将 promise 链分支为两个 promise 链,因为一旦调用者调用 myFetch
:
myFetch("localhost").then(request => { /* use request */ } );
然后 promise
将 .then
调用它两次(一次在 myFetch
中进行控制台日志记录,然后在此处再次调用)。
这很好。您可以根据需要多次调用 .then
相同的承诺,只要 promise
解析,这些函数就会以相同的顺序一起执行。
但是,重要的是,每个函数都代表原始承诺的一个分支,独立于所有其他分支。这就是为什么你不需要 return 或在你的 console.log
之后重新抛出任何东西:没有人在那个分支上监听,具体来说,myFetch
的调用者不受影响。
恕我直言,这非常适合记录日志,但在执行更多操作时需要注意细微的时间和错误处理差异:
var log = msg => div.innerHTML += msg + "<br>";
var myFetch = url => {
var p = Promise.resolve({});
p.then(() => log("a")).then(() => log("b"));
return p;
}
myFetch().then(() => log("1")).then(() => log("2")).catch(log); // a,1,b,2
<div id="div"></div>
这会发出 a,1,b,2
。如您所见,这里有两条链条在并行推进。当您考虑 promise
何时解决时,这是有道理的,但它可能会令人惊讶。
另一个微妙之处是错误处理也是按分支进行的(一个分支永远不会使另一个分支失败)。其实上面的代码是有bug的。你发现了吗?在 .then(() => log("b"))
之后应该有一个 catch
,否则在某些环境中,您在该分支中所做的任何事情的错误最终都无法处理或被吞没。
我有一些采用现有承诺的 javasript 代码 (例如,承诺 return 由 fetch() 编辑)并增加价值 (比如,then/catch 个用于调试的侦听器,或者更多):
let myFetch = function(url) {
return fetch(url).then(function(value) {
console.log("fetch succeeded: value=",value);
return value;
}.catch(function(reason) {
console.log("fetch failed: reason=",reason);
throw reason;
});
};
我发现自己修改了上面的代码,以便只有在某些条件为真时才添加侦听器:
let myFetch = function(url) {
let promise = fetch(url);
if (some condition) {
promise = promise.then(function(value) {
console.log("fetch succeeded: value=",value);
return value;
}.catch(function(reason) {
console.log("fetch failed: reason=",reason);
throw reason;
});
}
return promise;
};
现在我想知道,return 由 "then" 编辑的新承诺 return 对 myFetch 真的有意义吗 (实际上是 shorthand 另一个 "then")如上所述, 还是 return 最初的承诺(增加听众)更有意义? 换句话说,我正在考虑省略第二个 "promise =", 这样代码看起来像这样:
let myFetch = function(url) {
let promise = fetch(url);
if (some condition) {
promise.then(function(value) {
console.log("fetch succeeded: value=",value);
return value;
}.catch(function(reason) {
console.log("fetch failed: reason=",reason);
throw reason;
});
}
return promise;
};
这与以前的版本有明显不同吗? 哪个版本更可取,如果是,为什么?
好吧,如果您的成功处理程序 return
是值而您的拒绝处理程序 throw
是错误 - 那么它基本上就是承诺的身份转换。
您不仅不需要这样做 promise = promise.then
您甚至不需要 return 值:
let myFetch = function(url) {
let promise = fetch(url);
if (some condition) {
promise.then(function(value) {
console.log("fetch succeeded: value=",value);
}.catch(function(reason) {
console.log("fetch failed: reason=",reason);
});
}
return promise;
};
也就是说,如果您使用的是 ES6 和 let,您可以使用箭头函数,这无论如何都会使它变得更好:
let myFetch = function(url) {
let promise = fetch(url);
if (some condition) {
promise.then(value => console.log("fetch succeeded: value=",value))
.catch(reason => console.log("fetch failed: reason=",reason));
}
return promise;
};
像 bluebird 这样的一些 promise 库为此提供了一个 tap 实用程序。唯一的问题是,如果 fetch
添加了对 promise 取消的支持,那么如果你不链接它,你就会用 if (some condition)
处理程序打破链。
如果您的唯一用例是在 then
/catch
中记录某些内容 – 只要一切顺利,这应该无关紧要。如果出现异常,事情会变得更加混乱。考虑这两个例子:
Return原承诺
function myFetch() {
let promise = new Promise(function (resolve, reject) {
resolve(100);
});
promise.then(function () { throw new Error('x'); });
return promise;
}
myFetch().then(function () {
console.log('success!');
}).catch(function (e) {
console.error('error!', e)
});
结果是 success
并且在内部 then
中抛出的错误可能会在一些 promise 库中被吞没(尽管最流行的库如 Bluebird 处理这个并且你会得到额外的错误 Unhandled rejection Error: x
).
使用 native Promises in some environments.
Return正在修改承诺
function myFetch() {
let promise = new Promise(function (resolve, reject) {
resolve(100);
});
promise = promise.then(function () { throw new Error('x'); });
return promise;
}
myFetch().then(function () {
console.log('success!');
}).catch(function (e) {
console.error('error!', e)
});
现在结果是error! Error: x
。
你承诺分支。在第二种情况下,您实际上将 promise 链分支为两个 promise 链,因为一旦调用者调用 myFetch
:
myFetch("localhost").then(request => { /* use request */ } );
然后 promise
将 .then
调用它两次(一次在 myFetch
中进行控制台日志记录,然后在此处再次调用)。
这很好。您可以根据需要多次调用 .then
相同的承诺,只要 promise
解析,这些函数就会以相同的顺序一起执行。
但是,重要的是,每个函数都代表原始承诺的一个分支,独立于所有其他分支。这就是为什么你不需要 return 或在你的 console.log
之后重新抛出任何东西:没有人在那个分支上监听,具体来说,myFetch
的调用者不受影响。
恕我直言,这非常适合记录日志,但在执行更多操作时需要注意细微的时间和错误处理差异:
var log = msg => div.innerHTML += msg + "<br>";
var myFetch = url => {
var p = Promise.resolve({});
p.then(() => log("a")).then(() => log("b"));
return p;
}
myFetch().then(() => log("1")).then(() => log("2")).catch(log); // a,1,b,2
<div id="div"></div>
这会发出 a,1,b,2
。如您所见,这里有两条链条在并行推进。当您考虑 promise
何时解决时,这是有道理的,但它可能会令人惊讶。
另一个微妙之处是错误处理也是按分支进行的(一个分支永远不会使另一个分支失败)。其实上面的代码是有bug的。你发现了吗?在 .then(() => log("b"))
之后应该有一个 catch
,否则在某些环境中,您在该分支中所做的任何事情的错误最终都无法处理或被吞没。