问题描述
为了更好地了解 Javascript 中的 Promise 是如何工作的,我决定尝试一下并自己编写基本实现.
To gain better understanding of how promises work in Javascript I decided to give it a try and code basic implementation myself.
基本上我想实现将函数作为参数的 Promises 对象(我在我的代码中称之为 Aaa).这个函数可以调用resolve来resolve
承诺,或者reject到reject
.基本实现和用法如下.根据承诺规范,不确定第二个参数是否可以接受,但这就是我目前得到的.
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");
});
所以现在可以创建、调用和解析 promise.每个 then
方法都将返回新的 Aaa(承诺),因此可以链接它们.现在下面的代码使用上面创建的 promise 并链接 then
回调.每个 then
返回新的承诺,在这种情况下它似乎工作正常:
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
然而,问题是当 then
调用之一返回承诺时:
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
输出的调用不应该被调用,直到我们刚刚添加的返回的承诺被解决.
The call then where ddd
is output should not be called UNTIL the returned promise we just added is resolved.
如何最好地实施?
推荐答案
这里有很多情况您没有处理.最好的办法是首先将 promise 构建为状态机:
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 = [];
}
现在让我们定义一个简单的帮助器,以在我们的其余实现中使用:
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 (value && (typeof value === 'object' || typeof value === '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;
}
}
注意 resolve
如何接收一个 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 只能被完成/拒绝一次.我们也有第三方 Promise 可能行为不端的问题,我们应该保护我们的代码免受这种情况的影响.出于这个原因,我不仅在 resolve
中调用了 result.then(resolve, reject)
.相反,我将其拆分为一个单独的函数:
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
或 reject
,并且可能会抛出错误.我们需要处理所有这些情况(这就是 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 实现,但是,正如您在构建自己的实现时已经注意到的那样,我们需要一个返回 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
接受一个 Promise 并等待它被解决.
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.
注意我还没有测试过这个 Promise 实现(尽管据我所知它是正确的).您应该针对 Promises/A+ 测试套件 (https://github/promises-aplus/promises-tests),也可以找到 Promises/A+ 规范(https://github/promises-aplus/promises-spec) 有助于找出算法的任何特定部分的正确行为.作为最终资源,promise 是 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/promises-aplus/promises-tests) and may also find the Promises/A+ spec (https://github/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 promise 实现尝试的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
更多推荐
[db:关键词]
发布评论