基本的Javascript承诺实现尝试 [英] Basic Javascript promise implementation attempt
问题描述
为了更好地理解promises如何在Javascript中工作,我决定自己试一试代码基本实现。
To gain better understanding of how promises work in Javascript I decided to give it a try and code basic implementation myself.
基本上我想实现Promises Object(在我的代码中称之为Aaa),它将函数作为参数。此函数可以调用解析解决
的承诺,或拒绝拒绝
它。基本实现和用法如下。不确定第二个参数是否可以根据承诺规格接受,但这就是我到目前为止所得到的。
Basically I want to implement Promises Object (I call it Aaa in my code) that takes function as an argument. This function can call resolve to resolve
the promise, or reject to reject
it. The basic implementation and usage is below. Not sure if the second argument is accepteable according to promise specs, but that's what I got so far.
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");
});
所以现在可以创建,调用和解析承诺。每个然后
方法将返回新的Aaa(Promise),以便这些可以链接。现在,下面的代码使用上面创建的promise和chain 然后
回调。每个然后
返回新的承诺,在这种情况下它似乎工作正常:
So now the promise can be created, called and resolved. Each then
method will return new Aaa (Promise) so these can be chained. Now the code below uses promise created above and chains then
callbacks. Each then
returns new promise and in this case it seems to work fine:
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
然而问题是当其中一个然后
调用返回一个承诺:
The problem is however when one of the then
calls returns a 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已经解决。
The call then where ddd
is output should not be called UNTIL the returned promise we just added is resolved.
如何才能最好地实施?
How would that be best implemented?
推荐答案
有很多案例你没有在这里处理。最好的办法是首先将承诺建立为状态机:
There are a number of cases you're not handling here. The best bet is to start by building the promise as a state machine:
var PENDING = 0;
var FULFILLED = 1;
var REJECTED = 2;
function Promise() {
// store state which can be PENDING, FULFILLED or REJECTED
var state = PENDING;
// store value once FULFILLED or REJECTED
var value = null;
// store sucess & failure handlers
var handlers = [];
}
现在让我们定义一个简单的助手来完成其余的实现: / p>
Now lets define a simple helper to use through the rest of our implementation:
// a function that returns `then` if `value` is a promise, otherwise `null`
function getThen(value) {
if (result && (typeof result === 'object' || typeof result === 'function')) {
var then = value.then;
if (typeof then === 'function') {
return then;
}
}
return null;
}
接下来,我们需要考虑可能发生的每个转换:
Next, we need to consider each of the transformations that can occur:
var PENDING = 0;
var FULFILLED = 1;
var REJECTED = 2;
function Promise() {
// store state which can be PENDING, FULFILLED or REJECTED
var state = PENDING;
// store value once FULFILLED or REJECTED
var value = null;
// store sucess & failure handlers
var handlers = [];
function resolve(result) {
try {
var then = getThen(result);
if (then) {
doResolve(then.bind(result), resolve, reject)
return
}
state = FULFILLED;
value = result;
} catch (e) {
reject(e);
}
}
function reject(error) {
state = REJECTED;
value = error;
}
}
注意如何解决
可以接收Promise作为其参数,但Promise永远不能用另一个Promise来实现。所以我们必须处理这种特殊情况。
Note how resolve
can receive a Promise as its argument, but a Promise can never be fulfilled with another Promise. So we have to handle this special case.
另请注意,Promise只能被实现/拒绝一次。我们还有第三方承诺可能行为不端的问题,我们应该保护我们的代码。出于这个原因,我还没有从 resolve
中调用 result.then(解析,拒绝)
。相反,我把它拆分成一个单独的函数:
Note also that a Promise can only ever be fulfilled/rejected once. We also have the problem that a third party Promise may misbehave, and we should guard our code against that. For this reason, I haven't just called result.then(resolve, reject)
from within resolve
. Instead, I split that into a separate function:
/**
* Take a potentially misbehaving resolver function and make sure
* onFulfilled and onRejected are only called once.
*
* Makes no guarantees about asynchrony.
*/
function doResolve(fn, onFulfilled, onRejected) {
var done = false;
try {
fn(function (value) {
if (done) return
done = true
onFulfilled(value)
}, function (reason) {
if (done) return
done = true
onRejected(reason)
})
} catch (ex) {
if (done) return
done = true
onRejected(ex)
}
}
所以现在我们有一个完整的状态机,但无法观察或触发状态变化。让我们首先添加一种通过传入解析器函数来触发状态更改的方法。
So now we have a completed state machine, but no way to observe or trigger the changes in state. Lets start by adding a way to trigger the state changes by passing in a resolver function.
function Promise(fn) {
if (typeof this !== 'object')
throw new TypeError('Promises must be constructed via new');
if (typeof fn !== 'function')
throw new TypeError('fn must be a function');
// store state which can be PENDING, FULFILLED or REJECTED
var state = PENDING;
// store value once FULFILLED or REJECTED
var value = null;
// store sucess & failure handlers
var handlers = [];
function resolve(result) {
try {
var then = getThen(result);
if (then) {
doResolve(then.bind(result), resolve, reject)
return
}
state = FULFILLED;
value = result;
} catch (e) {
reject(e);
}
}
function reject(error) {
state = REJECTED;
value = error;
}
doResolve(fn, resolve, reject);
}
如您所见,我们重新使用 doResolve
因为我们有另一个不受信任的解析器。 fn
可能会多次调用 resolve
或拒绝
,以及它可能会引发错误。我们需要处理所有这些情况(这就是 doResolve
的情况)。
As you can see, we re-use doResolve
because we have another un-trusted resolver. The fn
might call resolve
or reject
multiple times, and it might throw an error. We need to handle all of these cases (and that's what doResolve
does).
我们现在已经完成了状态机器,但我们没有暴露任何有关它所处状态的信息。让我们尝试添加 .done(onFulfilled,onRejected)
方法,就像 .then
除了它没有返回Promise并且不处理 onFulfilled
和 onRejected抛出的错误
。
We now have the completed state machine, but we haven't exposed any information about what state it is in. Lets try adding a .done(onFulfilled, onRejected)
method that is just like .then
except that it does not return a Promise and does not handle errors thrown by onFulfilled
and onRejected
.
var PENDING = 0;
var FULFILLED = 1;
var REJECTED = 2;
function Promise(fn) {
if (typeof this !== 'object')
throw new TypeError('Promises must be constructed via new');
if (typeof fn !== 'function')
throw new TypeError('fn must be a function');
// store state which can be PENDING, FULFILLED or REJECTED
var state = PENDING;
// store value once FULFILLED or REJECTED
var value = null;
// store sucess & failure handlers
var handlers = [];
function resolve(result) {
try {
var then = getThen(result);
if (then) {
doResolve(then.bind(result), resolve, reject)
return
}
state = FULFILLED;
value = result;
handlers.forEach(handle);
handlers = null;
} catch (e) {
reject(e);
}
}
function reject(error) {
state = REJECTED;
value = error;
handlers.forEach(handle);
handlers = null;
}
function handle(handler) {
if (state === PENDING) {
handlers.push(handler);
} else {
if (state === FULFILLED && typeof handler.onFulfilled === 'function') {
handler.onFulfilled(value);
}
if (state === REJECTED && typeof handler.onRejected === 'function') {
handler.onRejected(value);
}
}
}
this.done = function (onFulfilled, onRejected) {
setTimeout(function () { // ensure we are always asynchronous
handle({
onFulfilled: onFulfilled,
onRejected: onRejected
});
}, 0);
}
doResolve(fn, resolve, reject);
}
注意我们必须处理 .done的情况
在Promise成就/拒绝之前和之后都被调用。
Note how we must handle the case of .done
being called both before and after the Promise becomes fulfilled/rejected.
我们几乎有一个完整的承诺实现,但是,正如你已经注意到的那样构建自己的实现,我们需要一个返回Promise的 .then
方法。
We almost have a complete promise implementation, but, as you already noticed when building your own implementation, we need a .then
method that returns a Promise.
我们可以轻松地构建这个 .done
:
We can build this easilly out of .done
:
this.then = function (onFulfilled, onRejected) {
var self = this;
return new Promise(function (resolve, reject) {
return self.done(function (result) {
if (typeof onFulfilled === 'function') {
try {
return resolve(onFulfilled(result));
} catch (ex) {
return reject(ex);
}
} else {
return resolve(result);
}
}, function (error) {
if (typeof onRejected === 'function') {
try {
return resolve(onRejected(error));
} catch (ex) {
return reject(ex);
}
} else {
return reject(error);
}
});
});
}
请注意我们如何获得您现在免费获得的东西,因为 resolve
接受承诺并等待其解决。
Note here how we get the thing you were struggling with for free now, because resolve
accepts a Promise and waits for it to be resolved.
NB 我没有没有测试过这个Promise的实现(尽管据我所知,它是正确的)。您应该测试针对Promises / A +测试套件构建的任何实现( https://github.com/ promises-aplus / promises-tests )也可以找到Promises / A +规范( https ://github.com/promises-aplus/promises-spec )用于确定算法的任何特定部分的正确行为。作为最终资源,承诺是Promise规范的一个非常小的实现。
N.B. I haven't tested this Promise implementation (although it is correct to the best of my knowledge). You should test any implementation you build against the Promises/A+ test suite (https://github.com/promises-aplus/promises-tests) and may also find the Promises/A+ spec (https://github.com/promises-aplus/promises-spec) useful in figuring out what the correct behavior is for any specific part of the algorithm. As a final resource, promise is a very minimal implementation of the Promise spec.
这篇关于基本的Javascript承诺实现尝试的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!