为了更好地理解promises如何在Javascript中工作,我决定尝试自己编写代码基本实现 .
基本上我想实现Promises Object(我在代码中称之为Aaa),它将函数作为参数 . 此函数可以调用解析 resolve
的承诺,或拒绝 reject
它 . 基本实现和用法如下 . 根据承诺规范,不确定第二个参数是否可接受,但这是我到目前为止所得到的 .
Aaa=function(f,pause) {
console.log("ggg");
var t=this;
this.f=f;
this.thens=[];
this.resolve=function(g) {
for(var i=0;i<t.thens.length;i++)
{
// try/catch to be used later for dealing with exceptions
try
{
t.thens[i].f(g);
t.thens[i].resolve();
}
catch(ex)
{}
}
};
// to be implemented later
this.reject=function(g) {};
this.then=function(resolve,reject) {
// i'm passing true for pause argument as we dont need to execute promise code just yet
var nextPromise=new Aaa(resolve,true);
this.thens.push(nextPromise);
return nextPromise;
}
if(!pause)
this.f(this.resolve,this.reject);
}
var aaa=new Aaa(function(resolve,reject) {
console.log("aaa");
setTimeout(function() {
console.log("fff");
resolve("good");
},2000);
console.log("bbb");
});
所以现在可以创建,调用和解决承诺 . 每个 then
方法都会返回新的Aaa(Promise),因此可以将它们链接起来 . 现在,下面的代码使用上面创建的promise和链 then
回调 . 每个 then
返回新的承诺,在这种情况下它似乎工作正常:
aaa.then(function(res) {
console.log("ccc");
console.log(res);
})
.then(function(res) {
console.log("ddd");
console.log(res);
},function(rej) {
console.log("eee");
console.log(rej);
});
我得到的输出是:
ggg
aaa
bbb
ggg
ggg
fff
ccc
good
ddd
undefined
但是,当 then
之一的调用返回一个promise时,问题是:
aaa.then(function(res) {
console.log("ccc");
console.log(res);
// here we return the promise manually. then next then call where "ddd" is output should not be called UNTIL this promise is resolved. How to do that?
return new Aaa(function(resolve,reject) {
console.log("iii");
setTimeout(function() {
console.log("kkk");
resolve("good2");
// reject("bad");
},2000);
console.log("jjj");
}).then(function (res) {
console.log("lll");
console.log(res);
});
})
.then(function(res) {
console.log("ddd");
console.log(res);
},function(rej) {
console.log("eee");
console.log(rej);
});
输出是:
ggg
aaa
bbb
ggg
ggg
fff
ccc
good
ggg
iii
jjj
ggg
ddd
undefined
kkk
lll
good2
然后不应该调用输出 ddd
的调用UNTIL我们刚刚添加的返回的promise将被解析 .
如何才能最好地实施?
4 回答
这一切看起来都非常复杂 . 我认为这是一个非常简单的递归解决方案 . 为了简洁起见,我将省略拒绝,但除了你停止链之外,它几乎与解决方案相同 .
你在这里处理的案例很多 . 最好的办法是将承诺 Build 为状态机:
现在让我们定义一个简单的帮助器,通过我们的其余实现来使用:
接下来,我们需要考虑可能发生的每个转换:
注意
resolve
如何接收Promise作为其参数,但是Promise永远不能用另一个Promise来实现 . 所以我们必须处理这个特例 .另请注意,Promise只能被实现/拒绝一次 . 我们还有第三方承诺可能行为不端的问题,我们应该保护我们的代码 . 出于这个原因,我还没有在
resolve
内调用result.then(resolve, reject)
. 相反,我把它分成一个单独的函数:所以现在我们有一个完整的状态机,但无法观察或触发状态的变化 . 让我们首先添加一种通过传入解析器函数来触发状态更改的方法 .
如您所见,我们重新使用
doResolve
因为我们有另一个不受信任的解析器 .fn
可能会多次调用resolve
或reject
,并且可能会抛出错误 . 我们需要处理所有这些情况(这就是doResolve
所做的) .我们现在已经完成了状态机,但是我们没有公开任何关于它所处状态的信息 . 让我们尝试添加
.done(onFulfilled, onRejected)
方法,就像.then
一样,除了它不返回Promise并且不处理onFulfilled
抛出的错误和onRejected
.注意我们必须处理在Promise变为满足/拒绝之前和之后调用
.done
的情况 .我们几乎有一个完整的promise实现,但是,正如您在构建自己的实现时已经注意到的那样,我们需要一个返回Promise的
.then
方法 .我们可以在
.done
中轻松构建这个:请注意我们如何获得您现在免费获得的东西,因为
resolve
接受了Promise并等待它被解决 .N.B. 我没有测试过这个Promise的实现(虽然据我所知是正确的) . 您应该测试针对Promises / A测试套件构建的任何实现(https://github.com/promises-aplus/promises-tests),并且还可以找到Promises / A规范(https://github.com/promises-aplus/promises-spec),用于确定算法的任何特定部分的正确行为 . 作为最终资源,promise是Promise规范的一个非常小的实现 .
(有关完整的Promise实现,请向下滚动) .
代码中的一些问题
有几个问题,但我认为你的代码中的主要错误是你将
then
方法的参数作为参数传递给一个新的promise:虽然两个参数都是回调函数,但它们具有不同的签名,并且用于完全不同的目的:
promise构造函数的参数是一个回调函数,它将立即同步执行 . 函数作为第一个参数传递给它,您可以使用它来解析您正在创建的承诺 .
then
方法的(第一个)参数是一个回调函数,只有在解析基本承诺时才会异步执行,并将解析后的值作为参数传递给它 .您可以在代码中看到差异,您可以将构造函数的参数存储为f属性 . 你有这两个:
...其中g是已解析的值,但也是:
......参数是函数 . 当你创建nextPromise时,你实际上首先用这两个参数调用f,然后用g参数调用 .
从头开始实现Promises / A兼容的实施
我们可以按照Promises/A+ specification中的要求构建我们自己的Promise实现:
2.1承诺状态
只允许2个状态转换:从挂起到履行,从挂起到拒绝 . 不可能进行其他转换,并且一旦执行转换,承诺值(或拒绝原因)不应更改 .
这是一个符合上述限制的简单实现 . 注释参考上述规范中的编号要求:
当然,这不提供
then
方法,这是Promises的关键:2.2当时的方法
这是规范的核心 . 上面的代码可以扩展为公开
then
方法,它返回一个promise并提供相应的then
回调的异步执行,只提供一次,提供多个then
调用,将异常转为拒绝,等等 .所以下面的代码添加了
then
方法,但也添加了一个单独定义的broadcast
函数,因为它必须在任何状态更改时调用:这不仅包括then
方法的效果(将promise添加到列表中),而且resolve
和reject
方法(状态和值的变化) .这几乎涵盖了所有内容,除了在
TODO:
评论中,必须调用所谓的Promise Resolution Procedure:2.3承诺解决程序
这是一个以不同方式处理值(甚至是promises)的过程:不是按原样返回值,程序将对该值执行
then
方法,并使用从then
回调接收的值异步履行保证 . 在规范中没有提到它,但这不仅在then
方法中执行,而且在使用这样的值解决主要承诺时也很有趣 .因此现有的
resolve
方法应该替换为"Promise Resolution Procedure",它将调用原始方法 . 最初的一个可以被称为"fulfill",表示它将解决承诺总是满足:现在这是Promises / A兼容的,至少它通过了测试套件 . 然而,Promise对象暴露了太多的方法和属性:
只有一个Promise对象
上面构建的构造函数创建的东西更像是Deferred对象,即暴露
resolve
和reject
方法 . 更糟糕的是,status
和value
属性是可写的 . 因此,将此视为不安全的Deferred对象的构造函数更合乎逻辑,并创建一个单独的Promise构造函数,该构造函数构建于此,但仅公开所需内容:then
方法和可以访问resolve
和reject
的构造函数回调 .然后,延迟对象可以不使用构造函数回调参数,并通过
promise
属性提供对纯promise对象的访问:这个代码有几种可能的优化,例如使Deferred方法成为私有函数,并将类似的代码合并到更短的代码块中,但是现在它很清楚地显示了每个需求的覆盖范围 .
快乐的编码 .
my solution