[英]Promise callbacks returning promises
With regard to these great two sources: NZakas - Returning Promises in Promise Chains and MDN Promises , I would like to ask the following: 关于这两个很重要的来源: NZakas - 承诺链中的承诺和MDN承诺 ,我想问以下问题:
Each time that we return a value from a promise fulfillment handler, how is that value passed to the new promise returned from that same handler? 每次我们从promise履行处理程序返回一个值时,该值是如何传递给从同一个处理程序返回的新promise?
For instance, 例如,
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
});
In this example, p2
is a promise. 在这个例子中, p2
是一个承诺。 p3
is also a promise originating from p1
's fulfillment handler. p3
也是源自p1
履行处理程序的承诺。 However p2 !== p3
. 但是p2 !== p3
。 Instead p2
somehow magically resolves to 43
(how?) and that value is then passed to p3
's fulfillment handler. 相反, p2
以某种方式神奇地解析为43
(如何?),然后将该值传递给p3
的履行处理程序。 Even the sentence here is confusing. 即使是这里的句子也令人困惑。
Could you please explain to me what exactly is going on here? 你能否向我解释一下究竟发生了什么? I am totally confused over this concept. 我对这个概念感到困惑。
Let's say that throwing inside then()
callback rejects the result promise with a failure, and returning from then()
callback fulfills the result promise with a success value. 假设在then()
回调中抛出会因失败而拒绝结果promise,而从then()
回调返回会以成功值实现结果promise。
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
But sometimes, even inside the continuation, we don't know whether we have succeeded or not. 但有时,即使在延续期内,我们也不知道我们是否成功。 We need more time. 我们需要更多时间。
return checkCache().then(cachedValue => {
if (cachedValue) {
return cachedValue
}
// I want to do some async work here
})
However, if I do async work there, it would be too late to return
or throw
, wouldn't it? 但是,如果我在那里进行异步工作,那么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
})
})
This is why Promises wouldn't be useful if you couldn't resolve to another Promise . 这就是为什么如果你无法解决另一个Promise, Promises就没用了。
It doesn't mean that in your example p2
would become p3
. 这并不意味着在你的例子中p2
会变成 p3
。 They are separate Promise objects. 它们是单独的Promise对象。 However, by returning p2
from then()
that produces p3
you are saying “I want p3
to resolve to whatever p2
resolves, whether it succeeds or fails”. 但是,通过从生成p3
then()
返回p2
,你会说“我希望p3
解析为任何p2
解析,无论是成功还是失败”。
As for how this happens, it's implementation-specific. 至于如何发生这种情况,它是特定于实现的。 Internally you can think of then()
as creating a new Promise. 在内部,您可以将then()
视为创建新的Promise。 The implementation will be able to fulfill or reject it whenever it likes. 实施将能够随时满足或拒绝它。 Normally, it will automatically fulfill or reject it when you return: 通常情况下,当您返回时,它会自动完成或拒绝它:
// 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
})
}
Again, this is very much pseudo-code but shows the idea behind how then()
might be implemented in Promise implementations. 同样,这是非常多的伪代码,但展示了如何在Promise实现中实现then()
的想法。
If we want to add support for resolving to a Promise, we just need to modify the code to have a special branch if the callback
you pass to then()
returned a Promise: 如果我们想要添加对Promise的解析支持,我们只需要修改代码以便在传递给then()
的callback
返回Promise时有一个特殊的分支:
if (succeeded) {
// If your callback returned a value,
// resolve the returned Promise to it...
if (typeof result.then === 'function') {
// ...unless it is a Promise itself,
// in which case we just pass our internal
// resolve and reject to then() of that Promise
result.then(resolve, reject)
} else {
resolve(result)
}
} else {
// If your callback threw an error,
// reject the returned Promise with it
reject(error)
}
})
Let me clarify again that this is not an actual Promise implementation and has big holes and incompatibilities. 让我再次澄清,这不是一个真正的Promise实现,并且有很大的漏洞和不兼容性。 However it should give you an intuitive idea of how Promise libraries implement resolving to a Promise. 但是,它应该让您直观地了解Promise库如何实现解析Promise。 After you are comfortable with the idea, I would recommend you to take a look at how actual Promise implementations handle this . 在您对这个想法感到满意之后,我建议您先看看实际的Promise实现如何处理这个问题 。
Basically p3
is return
-ing an another promise : p2
. 基本上p3
正在return
另一个承诺: p2
。 Which means the result of p2
will be passed as a parameter to the next then
callback, in this case it resolves to 43
. 这意味着结果p2
将作为一个参数到下一个要传递then
回调,在这种情况下,解析为43
。
Whenever you are using the keyword return
you are passing the result as a parameter to next then
's callback. 当你正在使用的关键字return
您传递的结果作为参数传递给下一then
的回调。
let p3 = p1.then(function(value) {
// first fulfillment handler
console.log(value); // 42
return p2;
});
Your code : 你的代码:
p3.then(function(value) {
// second fulfillment handler
console.log(value); // 43
});
Is equal to: 等于:
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'
});
Btw, I've noticed that you are using ES6 syntax, you can have a lighter syntax by using fat arrow syntax : 顺便说一下,我注意到你使用的是ES6语法,你可以通过使用胖箭头语法来获得更轻的语法:
p1.then(resultOfP1 => p2) // the `return` is implied since it's a one-liner
.then(resultOfP2 => console.log(resultOfP2));
In this example, p2 is a promise. 在这个例子中,p2是一个承诺。 p3 is also a promise originating from p1's fulfillment handler. p3也是源自p1履行处理程序的承诺。 However p2 !== p3. 但是p2!== p3。 Instead p2 somehow magically resolves to 43 (how?) and that value is then passed to p3's fulfillment handler. 相反,p2以某种方式神奇地解析为43(如何?),然后将该值传递给p3的履行处理程序。 Even the sentence here is confusing. 即使是这里的句子也令人困惑。
a simplified version how this works (only pseudocode) 一个简化版本如何工作(只有伪代码)
function resolve(value){
if(isPromise(value)){
value.then(resolve, reject);
}else{
//dispatch the value to the listener
}
}
the whole thing is quite more complicated since you have to take care, wether the promise has already been resolved and a few more things. 整个事情要复杂得多,因为你必须要小心,承诺已经解决了,还有更多的东西。
I'll try to answer the question "why then
callbacks can return Promise
s themselves" more canonical. 我将尝试回答“为什么then
回调可以让Promise
自己回归”更为规范的问题。 To take a different angle, I compare Promise
s with a less complex and confusing container type - Array
s. 为了采取不同的角度,我将Promise
s与一个不太复杂和容易混淆的容器类型 - Array
s进行比较。
A Promise
is a container for a future value. Promise
是未来价值的容器。 An Array
is a container for an arbitrary number of values. Array
是任意数量值的容器。
We can't apply normal functions to container types: 我们不能将常规函数应用于容器类型:
const sqr = x => x * x;
const xs = [1,2,3];
const p = Promise.resolve(3);
sqr(xs); // fails
sqr(p); // fails
We need a mechanism to lift them into the context of a specific container: 我们需要一种机制将它们提升到特定容器的上下文中:
xs.map(sqr); // [1,4,9]
p.then(sqr); // Promise {[[PromiseValue]]: 9}
But what happens when the provided function itself returns a container of the same type? 但是当提供的函数本身返回相同类型的容器时会发生什么?
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
acts like expected. sqra
表现得像预期的那样。 It just returns a nested container with the correct values. 它只返回一个具有正确值的嵌套容器。 This is obviously not very useful though. 这显然不是很有用。
But how can the result of sqrp
be interpreted? 但是如何解释sqrp
的结果呢? If we follow our own logic, it had to be something like Promise {[[PromiseValue]]: Promise {[[PromiseValue]]: 9}}
- but it is not. 如果我们遵循自己的逻辑,那就必须像Promise {[[PromiseValue]]: Promise {[[PromiseValue]]: 9}}
- 但事实并非如此。 So what magic is going on here? 那么这里有什么魔力呢?
To reconstruct the mechanism we merely need to adapt our map
method a bit: 要重建机制,我们只需要调整我们的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
just takes a function and a value, applies the function to the value and unwraps the result, thus it reduces a nested array structure by one level. flatten
只接受一个函数和一个值,将函数应用于该值并展开结果,从而将嵌套数组结构减少一个级别。
Simply put, then
in the context of Promise
s is equivalent to map
combined with flatten
in the context of Array
s. 简单地说, then
在Promise
的上下文中,相当于在Array
的上下文中与flatten
结合的map
。 This behavior is extremely important. 这种行为非常重要。 We can apply not only normal functions to a Promise
but also functions that itself return a Promise
. 我们不仅可以将正常函数应用于Promise
,还可以将函数本身应用于Promise
。
In fact this is the domain of functional programming. 实际上,这是函数式编程的领域。 A Promise
is a specific implementation of a monad , then
is bind
/ chain
and a function that returns a Promise
is a monadic function. Promise
是monad的特定实现, then
是bind
/ chain
,返回Promise
的函数是monadic函数。 When you understand the Promise
API you basically understand all monads. 当您了解Promise
API时,您基本上了解所有monad。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.