Closed francecil closed 4 years ago
完整实现还是挺耗时的,这里仅以 then 方法为切入点,实现部分功能
先谈规范,再分析 polyfill 源码,最后实例解析。
规范直接参考这边:【翻译】Promises/A+规范
返回值这部分重要且易错,这里单独提出来分析下
首先,then 方法必须返回一个 promise 对象
promise2 = promise1.then(onFulfilled, onRejected);
[[Resolve]](promise2, x)
e.g.:
promise1 = Promise.resolve(1);onFulfilled = 2;
\ 则promise2 = Promise {<resolved>: 1}
e.g.:
promise1 = Promise.reject(1);onRejected = 2;
\ 则promise2 = Promise {<rejected>: 1}
e.g.:
promise1 = Promise.reject(1);onFulfilled =()=>1; onRejected=()=>2;
\ 则promise2 = Promise {<resolved>: 2}
;e.g.:
promise1 = Promise.resolve(1);onFulfilled =()=>{throw new Error('test')}; onRejected=(e)=>console.log(e);
\ 则promise2 = Promise {<rejected>: Error: test
[[Resolve]](promise, x)
promise 为 then 回调的返回值,运行 [[Resolve]](promise, x)
需遵循以下步骤:
如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise \ e.g.:
test = {};test = Promise.resolve().then(()=>test)
\//Promise {<rejected>: TypeError: Chaining cycle detected for promise #<Promise>}
promise 接受x的状态 \ e.g.:
promise2 = Promise.resolve().then(()=>Promise.reject(1))
\//Promise {<rejected>: 1}
Promise.resolve().then(()=>({a:1,then:function (res,rej){res(this.a)}}))
//Promise {<resolved>: 1}
Promise.resolve().then(()=>({a:1,then:function (res,rej){rej(this.a)}}))
//Promise {<rejected>: 1}
以 x 为参数执行 promise
如果一个 promise 被一个循环的 thenable 链中的对象解决,而 [[Resolve]](promise, thenable) 的递归性质又使得其被再次调用,根据上述的算法将会陷入无限递归之中。算法虽不强制要求,但也鼓励施者检测这样的递归是否存在,若检测到存在则以一个可识别的 TypeError 为据因来拒绝 promise
这个目前还不知道怎么写才能产生循环的 thenable 链 - -
全局维护一个 microTask Queue ,每个 promise 自身维护一个 queue
当 promise 执行 then 方法时:
当同步代码执行完毕,开始执行 microTask Queue 中的任务
执行 microTask :
不断的执行 microTask 直到 microTask Queue 为空,一轮 event loop 结束
对 lie.js (一个 Promise polyfill 库) 的代码做个精简,仅考虑普通的 then 回调和 resolve 方法
然后 microTask 任务执行接口我们直接用的 setTimeout ,不搞 Mutation 那些幺蛾子,代码如下
// immediate.js
var scheduleDrain = function () {
setTimeout(nextTick, 0);
};
var draining;
var queue = [];
// 在同步代码 task 执行之后,模拟清空 microTask queue
// 由于执行过程中可能有新的 microTask 所以用了双层循环
function nextTick() {
draining = true;
var i, oldQueue;
var len = queue.length;
while (len) {
oldQueue = queue;
queue = [];
i = -1;
while (++i < len) {
oldQueue[i]();
}
len = queue.length;
}
draining = false;
}
function immediate(task) {
if (queue.push(task) === 1 && !draining) {
scheduleDrain();
}
}
Promise 代码如下
// promise.js
// then 方法执行时作为参数 resolver 实例化 Promise
function INTERNAL () { }
const REJECTED = 'REJECTED';
const FULFILLED = 'FULFILLED';
const PENDING = 'PENDING';
function Promise (resolver) {
if (typeof resolver !== 'function') {
throw new TypeError('resolver must be a function');
}
this.state = PENDING;
this.queue = [];
this.outcome = void 0;
// 外部通过 new 实例化时执行
if (resolver !== INTERNAL) {
safelyResolveThenable(this, resolver);
}
}
Promise.prototype.then = function (onFulfilled, onRejected) {
// 特殊参数的处理
if (typeof onFulfilled !== 'function' && this.state === FULFILLED ||
typeof onRejected !== 'function' && this.state === REJECTED) {
return this;
}
// 创建一个 PENDING 状态的 promise
var promise = new this.constructor(INTERNAL);
// 根据原 promise 状态进行不同处理
if (this.state !== PENDING) {
var resolver = this.state === FULFILLED ? onFulfilled : onRejected;
makeMicroTask(promise, resolver, this.outcome);
} else {
this.queue.push(new QueueItem(promise, onFulfilled, onRejected));
}
return promise;
};
Promise.resolve = function (value) {
if (value instanceof this) {
return value;
}
return handleResolve(new this(INTERNAL), value);
}
// promsie 内部队列项
function QueueItem (promise, onFulfilled, onRejected) {
this.promise = promise;
// 仅考虑 function类型的 onFulfilled
this.onFulfilled = onFulfilled;
this.callFulfilled = function(value) {
makeMicroTask(this.promise, this.onFulfilled, value);
};
// 暂不处理 onRejected
}
// 将 handleResolve 操作放入 microtask
function makeMicroTask (promise, func, value) {
immediate(function () {
handleResolve(promise, func(value));
});
}
// 改变 promise 状态,并通知 queue 中的 promise 执行 makeMicroTask
function handleResolve (self, value) {
self.state = FULFILLED;
self.outcome = value;
var i = -1;
var len = self.queue.length;
while (++i < len) {
self.queue[i].callFulfilled(value);
}
return self;
}
// 对 resolver 方法的两个参数(resolve,reject)进行包装
function safelyResolveThenable (self, thenable) {
var called = false;
function onError (value) {
if (called) {
return;
}
called = true;
// 暂不处理 reject
// handlers.reject(self, value);
}
function onSuccess (value) {
if (called) {
return;
}
called = true;
handleResolve(self, value);
}
thenable(onSuccess, onError);
}
new Promise(resolve => {
console.log(1);
resolve(3);
Promise.resolve().then(()=> console.log(4))
}).then(num => {
console.log(num)
});
console.log(2)
Promise.resolve().then(() => {
console.log(1);
Promise.resolve().then(()=> console.log(4))
}).then(() => {
console.log(3)
});
console.log(2)
new Promise((r) => {
r()
console.log(1);
}).then(() => {
console.log(11)
}).then(() => {
console.log(12)
}).then(() => {
console.log(13)
})
var promise = new Promise((r) => {
r()
console.log(2);
let promise = Promise.resolve().then(()=> console.log(21)).then(()=> console.log(22)).then(()=> console.log(23))
promise.then(()=>console.log(29))
Promise.resolve().then(()=> console.log(24)).then(()=> console.log(25)).then(()=> console.log(26))
})
promise.then(() => {
console.log(27)
})
promise.then(() => {
console.log(28)
})
当我们的 polyfill 写完后,需要测试下是否符合规范,我们自己写测试用例肯定是不可能的,这里有一个测试库 promises-tests
const tests = require("promises-aplus-tests");
const Promise = require("./index");
const deferred = function() {
let resolve, reject;
const promise = new Promise(function(_resolve, _reject) {
resolve = _resolve;
reject = _reject;
});
return {
promise: promise,
resolve: resolve,
reject: reject
};
};
const adapter = {
deferred
};
tests.mocha(adapter);
这样就可以做测试了
参考 es6-promise