technology-blog
technology-blog copied to clipboard
第 10 题:简单手写实现promise
// 简易版本的promise
// 第一步: 列出三大块 this.then resolve/reject fn(resolve,reject)
// 第二步: this.then负责注册所有的函数 resolve/reject负责执行所有的函数
// 第三步: 在resolve/reject里面要加上setTimeout 防止还没进行then注册 就直接执行resolve了
// 第四步: resolve/reject里面要返回this 这样就可以链式调用了
// 第五步: 三个状态的管理 pending fulfilled rejected
// *****promise的链式调用 在then里面return一个promise 这样才能then里面加上异步函数
// 加上了catch
function PromiseM(fn) {
var value = null;
var callbacks = [];
//加入状态 为了解决在Promise异步操作成功之后调用的then注册的回调不会执行的问题
var state = 'pending';
var _this = this;
//注册所有的回调函数
this.then = function (fulfilled, rejected) {
//如果想链式promise 那就要在这边return一个new Promise
return new PromiseM(function (resolv, rejec) {
//异常处理
try {
if (state == 'pending') {
callbacks.push(fulfilled);
//实现链式调用
return;
}
if (state == 'fulfilled') {
var data = fulfilled(value);
//为了能让两个promise连接起来
resolv(data);
return;
}
if (state == 'rejected') {
var data = rejected(value);
//为了能让两个promise连接起来
resolv(data);
return;
}
} catch (e) {
_this.catch(e);
}
});
}
//执行所有的回调函数
function resolve(valueNew) {
value = valueNew;
state = 'fulfilled';
execute();
}
//执行所有的回调函数
function reject(valueNew) {
value = valueNew;
state = 'rejected';
execute();
}
function execute() {
//加入延时机制 防止promise里面有同步函数 导致resolve先执行 then还没注册上函数
setTimeout(function () {
callbacks.forEach(function (cb) {
value = cb(value);
});
}, 0);
}
this.catch = function (e) {
console.log(JSON.stringify(e));
}
//经典 实现异步回调
fn(resolve, reject);
}
new PromiseM((res, rej) => { setTimeout(() => { rej() },0) }).then(function(){ console.log(111) }, function(){ console.log(222); }) //111
大神思路很清晰,不过上面代码还是有些bug 1:then 方法里面,在最初始的pending状态,只push了fulfilled的回调函数,就会导致,无论是res还是rej,打印的都是111,理论上,上述例子打印应该是222,callbacks可以分成2份进行存储
这里不支持多层then链式调用(then中可返回新的promise进行异步流控制),不支持finally,我写了个版本,多多指教 http://www.a4z.cn/fe/2019/04/18/weekly-fe-interview/
这里不支持多层then链式调用(then中可返回新的promise进行异步流控制),不支持finally,我写了个版本,多多指教 http://www.a4z.cn/fe/2019/04/18/weekly-fe-interview/
then: function (onFulfilled, onRejected) {
var _this = this
if (_this.promiseStatus == 'pending') {
return new Promise2(function (resolve, reject) {
onFulfilled && (_this.onFulfilledCb = function () {
try {
var x = onFulfilled(_this.promiseValue);
if (x instanceof Promise2) {
x.then(resolve, reject);
} else {
resolve(x);
}
} catch (e) {
reject(e)
}
});
onRejected && (_this.onRejectedCb = function () {
try {
var x = onRejected(_this.promiseValue);
if (x instanceof Promise2) {
x.then(resolve, reject);
} else {
resolve(x);
}
} catch (e) {
reject(e)
}
})
})
}
if (_this.promiseStatus == 'fulfilled') {
return new Promise2(function (resolve, reject) {
try {
var x = onFulfilled(_this.promiseValue);
if (x instanceof Promise2) {
x.then(resolve, reject);
} else {
resolve(x);
}
} catch (e) {
reject(e)
}
})
}
if (_this.promiseStatus == 'rejected') {
return new Promise2(function (resolve, reject) {
try {
var x = onRejected(_this.promiseValue);
if (x instanceof Promise2) {
x.then(resolve, reject);
} else {
resolve(x);
}
} catch (e) {
reject(e)
}
})
}
},
对你的实现then 做了如下改动,感觉是不需要轮询的
附加需求:
- catch完允许恢复
- then/catch的回调允许返回Promise
let isThenable = x => !!(x!==undefined && x.then);
let runThenable = (func, arg) => isThenable(arg) ? arg.then(func) : func(arg);
class MyPromise{
constructor(f){
this.succ_que = [];
this.fail_que = [];
this.done = false;
this.resolve = result => {
if(this.done) return;
this.result = result;
this.done = true;
setImmediate(() => { // setImmediate to prevent children from being caught by parent
this.succ_que.forEach(cb => cb(result));
});
};
this.reject = error => {
if(this.done) return;
this.error = error;
this.done = true;
setImmediate(() => {
this.fail_que.forEach(cb => cb(error));
});
};
this.then = (succ_cb, fail_cb) => new MyPromise((next_resolve, next_reject) => {
let handle_result = v => {
try{
runThenable(next_resolve, succ_cb ? succ_cb(v) : v); // runThenable to allow succ_cb/fail_cb return a promise
} catch (e) {
next_reject(e)
}
};
let handle_error = e => {
try{
if(fail_cb)
runThenable(next_resolve, fail_cb(e)); // resume after caught by fail_cb
else
next_reject(e);
}catch (e) {
next_reject(e)
}
};
if(this.done){
this.error ? handle_error(this.error) : handle_result(this.result)
}else{
this.succ_que.push(handle_result);
this.fail_que.push(handle_error);
}
});
this.catch = fail_cb => this.then(null, fail_cb);
try{
f(this.resolve, this.reject);
}catch (e) {
this.reject(e);
}
}
}
MyPromise.resolve = x => new MyPromise(r => r(x));
MyPromise.Any = (...promises) => {
let done = 0;
return new MyPromise(r => {
promises.forEach(p => p.then(v =>{ if(!done){done++; r(v);} }));
})
};
MyPromise.All = (...promises)=>{
let count = promises.length;
let values = [];
return new MyPromise((r, f) => {
promises.forEach((p, i) =>
p.then(v => {
values[i] = v;
count --;
if(count === 0) r(values);
}, f)
)
});
};
// test:
MyPromise.resolve(1)
.then(v=> v+1)
.catch(e => console.log(`won't happen error: ${e}`))
.then(v => {console.log(`continued: ${v}`); throw new Error("throw");})
.then(v => {console.log("won't happen then");})
.catch(e => {console.log(`catched: ${e}`); return 100;})
.then(v => {console.log(`continue after catch: ${v}`); return v;})
.then(v => new MyPromise(r=> setTimeout(() => r(v+500), 3000)))
.then(v => console.log(`last: ${v}`))
;
console.log("===========");
有几个疑问哈... 如果同步链式then调用 2次肯定都是pending 但是 callbacks 里面只push了 fulfilled,这样导致链式调用失效了,简单粗暴解决 callbacks 里面 callbacks.push(fulfilled, resolv) 不知可行否 还有就是这里没有区分 reject 情况吧, 按照这个例子来是不是要区分 resolve callback 和 reject callback ?
附加需求:
- catch完允许恢复
- then/catch的回调允许返回Promise
let isThenable = x => !!(x!==undefined && x.then); let runThenable = (func, arg) => isThenable(arg) ? arg.then(func) : func(arg); class MyPromise{ constructor(f){ this.succ_que = []; this.fail_que = []; this.done = false; this.resolve = result => { if(this.done) return; this.result = result; this.done = true; setImmediate(() => { // setImmediate to prevent children from being caught by parent this.succ_que.forEach(cb => cb(result)); }); }; this.reject = error => { if(this.done) return; this.error = error; this.done = true; setImmediate(() => { this.fail_que.forEach(cb => cb(error)); }); }; this.then = (succ_cb, fail_cb) => new MyPromise((next_resolve, next_reject) => { let handle_result = v => { try{ runThenable(next_resolve, succ_cb ? succ_cb(v) : v); // runThenable to allow succ_cb/fail_cb return a promise } catch (e) { next_reject(e) } }; let handle_error = e => { try{ if(fail_cb) runThenable(next_resolve, fail_cb(e)); // resume after caught by fail_cb else next_reject(e); }catch (e) { next_reject(e) } }; if(this.done){ this.error ? handle_error(this.error) : handle_result(this.result) }else{ this.succ_que.push(handle_result); this.fail_que.push(handle_error); } }); this.catch = fail_cb => this.then(null, fail_cb); try{ f(this.resolve, this.reject); }catch (e) { this.reject(e); } } } MyPromise.resolve = x => new MyPromise(r => r(x)); MyPromise.Any = (...promises) => { let done = 0; return new MyPromise(r => { promises.forEach(p => p.then(v =>{ if(!done){done++; r(v);} })); }) }; MyPromise.All = (...promises)=>{ let count = promises.length; let values = []; return new MyPromise((r, f) => { promises.forEach((p, i) => p.then(v => { values[i] = v; count --; if(count === 0) r(values); }, f) ) }); }; // test: MyPromise.resolve(1) .then(v=> v+1) .catch(e => console.log(`won't happen error: ${e}`)) .then(v => {console.log(`continued: ${v}`); throw new Error("throw");}) .then(v => {console.log("won't happen then");}) .catch(e => {console.log(`catched: ${e}`); return 100;}) .then(v => {console.log(`continue after catch: ${v}`); return v;}) .then(v => new MyPromise(r=> setTimeout(() => r(v+500), 3000))) .then(v => console.log(`last: ${v}`)) ; console.log("===========");
对于这里的 Promise then 里面 done 部分处理有点不解。 这样处理会使得同步resolve和 Native 的 Promise 不一样。 new Promise((resolve, reject) => { result.push(1); resolve(2); result.push(3); }).then((d) => { result.push(d); }); setTimeout(() => { result.push(4); }, 0); result.push(5);
最后结果应该是 1, 3, 5, 2, 4
如果在then 里面因为是同步操作就done的话 then 的 cb 不会进入到 succ_cb中 输出结果是 1, 3, 2, 5, 4 这样和native 的promise 表现不一致了