关于这两个很重要的来源:NZakas - Returning Promises in Promise Chains和MDN Promises,我想问一下:
每次我们从promise履行处理程序返回一个值时,该值如何传递给从同一个处理程序返回的新promise?
例如,
let p1 = new Promise(function(resolve, reject) {
resolve(42);
});
let p2 = new Promise(function(resolve, reject) {
resolve(43);
});
let p3 = p1.then(function(value) {
// first fulfillment handler
console.log(value); // 42
return p2;
});
p3.then(function(value) {
// second fulfillment handler
console.log(value); // 43
});
在此示例中,p2
是一个承诺。 p3
也是来自p1
履行处理程序的承诺。但是p2 !== p3
。相反,p2
以某种方式神奇地解析为43
(如何?),然后将该值传递给p3
的履行处理程序。即使是这里的句子也令人困惑。
您能否向我解释一下究竟发生了什么?我对这个概念感到很困惑。
答案 0 :(得分:36)
假设抛出then()
回调内部会因失败而拒绝结果承诺,并且从then()
回调返回会以成功值来履行结果承诺。
let p2 = p1.then(() => {
throw new Error('lol')
})
// p2 was rejected with Error('lol')
let p3 = p1.then(() => {
return 42
})
// p3 was fulfilled with 42
但有时,即使在延续期内,我们也不知道我们是否成功。我们需要更多时间。
return checkCache().then(cachedValue => {
if (cachedValue) {
return cachedValue
}
// I want to do some async work here
})
但是,如果我在那里进行异步工作,那么return
或throw
为时已晚,不是吗?
return checkCache().then(cachedValue => {
if (cachedValue) {
return cachedValue
}
fetchData().then(fetchedValue => {
// Doesn’t make sense: it’s too late to return from outer function by now.
// What do we do?
// return fetchedValue
})
})
如果你不能解析另一个Promise ,这就是Promises没用的原因。
这并不意味着您的示例p2
p3
。它们是单独的Promise对象。但是,通过返回p2
生成then()
的{{1}},您会说“我希望p3
解析为p3
解析,是否成功或者失败“。
至于 这是怎么发生的,它是特定于实现的。在内部,您可以将p2
视为创建新的Promise。实施将能够随时满足或拒绝它。通常,当您返回时,它会自动履行或拒绝它:
then()
同样,这是非常多的伪代码,但显示了如何在Promise实现中实现// Warning: this is just an illustration
// and not a real implementation code.
// For example, it completely ignores
// the second then() argument for clarity,
// and completely ignores the Promises/A+
// requirement that continuations are
// run asynchronously.
then(callback) {
// Save these so we can manipulate
// the returned Promise when we are ready
let resolve, reject
// Imagine this._onFulfilled is an internal
// queue of code to run after current Promise resolves.
this._onFulfilled.push(() => {
let result, error, succeeded
try {
// Call your callback!
result = callback(this._result)
succeeded = true
} catch (err) {
error = err
succeeded = false
}
if (succeeded) {
// If your callback returned a value,
// fulfill the returned Promise to it
resolve(result)
} else {
// If your callback threw an error,
// reject the returned Promise with it
reject(error)
}
})
// then() returns a Promise
return new Promise((_resolve, _reject) => {
resolve = _resolve
reject = _reject
})
}
背后的想法。
如果我们想要添加对Promise的解析支持,我们只需要修改代码,以便在传递给then()
的{{1}}返回Promise时有一个特殊的分支:
callback
让我再次澄清,这不是一个真正的Promise实现,并且有很大的漏洞和不兼容性。但是,它应该让您直观地了解Promise库如何实现解析Promise。在您对这个想法感到满意之后,我建议您先看看实际的Promise实现方式handle this。
答案 1 :(得分:17)
基本上p3
是return
- 另一个承诺:p2
。这意味着p2
的结果将作为参数传递给下一个then
回调,在这种情况下,它会解析为43
。
每当您使用关键字return
时,您都会将结果作为参数传递给下一个then
的回调。
let p3 = p1.then(function(value) {
// first fulfillment handler
console.log(value); // 42
return p2;
});
您的代码:
p3.then(function(value) {
// second fulfillment handler
console.log(value); // 43
});
等于:
p1.then(function(resultOfP1) {
// resultOfP1 === 42
return p2; // // Returning a promise ( that might resolve to 43 or fail )
})
.then(function(resultOfP2) {
console.log(resultOfP2) // '43'
});
顺便说一句,我注意到您使用的是ES6语法,使用胖箭头语法可以获得更轻的语法:
p1.then(resultOfP1 => p2) // the `return` is implied since it's a one-liner
.then(resultOfP2 => console.log(resultOfP2));
答案 2 :(得分:3)
在这个例子中,p2是一个承诺。 p3也是源自p1履行处理程序的承诺。但是p2!== p3。相反,p2以某种方式神奇地解析为43(如何?),然后将该值传递给p3的履行处理程序。即使是这里的句子也令人困惑。
简化版本如何工作(仅伪代码)
require_once("simpledom.php");
$url="http://google.com";
$sPage = file_get_contents($url);
$sPageContent = new simple_html_dom();
$sPageContent->load($sPage);
$sObjects = $sPageContent->find('unknown');
foreach($sObjects as $sKey)
{
var_dump($sKey->_[4]); /*this var_dump shows the stuff */
$showres = $sKey->_[4]
}
/* this variable should hold the string but it shows nothing */
echo $showres;
整个事情变得更加复杂,因为你必须要小心,承诺已经解决了,还有更多的东西。
答案 3 :(得分:2)
我会尝试回答问题"为什么then
回调可以返回Promise
自己" 更具规范性。为了采取不同的角度,我将Promise
与较不复杂且容易混淆的容器类型进行比较 - Array
s。
Promise
是未来值的容器。
Array
是任意数量值的容器。
我们无法将正常功能应用于容器类型:
const sqr = x => x * x;
const xs = [1,2,3];
const p = Promise.resolve(3);
sqr(xs); // fails
sqr(p); // fails
我们需要一种机制将它们提升到特定容器的上下文中:
xs.map(sqr); // [1,4,9]
p.then(sqr); // Promise {[[PromiseValue]]: 9}
但是当提供的函数本身返回相同类型的容器时会发生什么?
const sqra = x => [x * x];
const sqrp = x => Promise.resolve(x * x);
const xs = [1,2,3];
const p = Promise.resolve(3);
xs.map(sqra); // [[1],[4],[9]]
p.then(sqrp); // Promise {[[PromiseValue]]: 9}
sqra
表现得像预期的那样。它只返回一个具有正确值的嵌套容器。这显然不是很有用。
但是如何解释sqrp
的结果呢?如果我们遵循自己的逻辑,它必须是Promise {[[PromiseValue]]: Promise {[[PromiseValue]]: 9}}
- 但事实并非如此。那么这里有什么魔力?
要重建机制,我们只需要稍微调整map
方法:
const flatten = f => x => f(x)[0];
const sqra = x => [x * x];
const sqrp = x => Promise.resolve(x * x);
const xs = [1,2,3];
xs.map(flatten(sqra))
flatten
只接受一个函数和一个值,将函数应用于该值并展开结果,从而将嵌套数组结构减少一级。
简单地说,在then
s的上下文中,Promise
相当于map
在flatten
s的上下文中与Array
相结合。这种行为非常重要。 我们不仅可以将正常函数应用于Promise
,还可以将函数本身返回Promise
。
实际上这是函数式编程的领域。 Promise
是 monad 的具体实现,then
是bind
/ chain
,返回Promise
的函数是一元函数。当您了解Promise
API时,您基本上了解所有monad。