Promise很复杂..恩..原来觉得不就是词法作用域吗, 原来觉得词法作用域不就是调用时的堆栈是定义时的嘛 结果...一个简单概念被玩成了这样..
promise.js源码从https://blog.csdn.net/qq_22844483/article/details/73655738
所得 谢谢原作者(未申请转载 啊哈) 原本他的意思是从0写一个promise 但是promise的链式调用部分看着晕晕的 为什么呢 因为正向来考虑像我这种菜鸟想不通啊 也罢 那就逆向来想吧 跟一下堆栈 没成想 两个then就20多个堆栈 一步一步跟我来 反向完了来正向 非把它搞定不可
使用的promise.js是简版 只有resolve而没有其他实现(包括.all/reject等)
先上一段使用代码 两个.then
![图片描述](//img.mukewang.com/5be113d10001170c08070626.jpg)
在最后的console.log打断点 看到的堆栈是这样的
![图片描述](//img.mukewang.com/5be113e70001a3a706980824.jpg)
二十几个啊 咳咳 看看多少同名的玩意....
在promise.js中加了一些有意义的输出 控制台是这样的(图三)
![图片描述](//img.mukewang.com/5be1140c000132e812790680.jpg)
这里声明:
①②③④⑤⑥⑦⑧⑨⑩...为标记 可以通过标记符在很多行的文字解释和代码之间查找 没办法 promise里各种同名不同栈的函数..
上promise.js源码(使用的是https://blog.csdn.net/qq_22844483/article/details/73655738 里的部分代码 转载未申请 好开心)
<pre>
var i = 0;
function Promisee(fn) {
var state = 'pending',
value = null,
callbacks = [],
aa = ++i; ②
Promisee.ii = i, ①
this.then = function thenFun(onFulfilled) {
return new Promisee(function resolveFuc(resolve) {
handle({
onFulfilled: onFulfilled || null,
resolve: resolve
});
});
};
function handle(callback) {
if (state === 'pending') {
callbacks.push(callback);
console.log('状态是pending',"这是Promisee第",handle.caller.caller.ii,"次执行时执行的handle方法 当然 handle是在Promisee第", aa,"次定义的");//说明调用handle的是this.then中的Promisee
console.log('push', callbacks);
return;
}
//如果then中没有传递任何东西
if(!callback.onFulfilled) {
callback.resolve(value);
return;
}
console.log('状态是fulfilled','这是第',aa,'个Promisee里的handle');
var ret = callback.onFulfilled(value); ⑨
callback.resolve(ret);
}
function resolve(newValue) {
console.log('这是第',aa,'个Promisee里的resolve');
if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {
var then = newValue.then;
if (typeof then === 'function') {
then.call(newValue, resolve);
return;
}
}
if(newValue == undefined){
console.log('\tvalue等于undefined了 还是在resolve内执行');
if(callbacks.length == 0)
console.log('\t啥都木有');
return;
}
state = 'fulfilled';
value = newValue;
setTimeout(function setTimeoutFuc() {
callbacks.forEach(function (callback) {
console.log('pop', callbacks);
handle(callback);
});
}, 0);
}
fn(resolve); ⑥
}
</pre>
对照执行代码看下
<pre>
var a = function(){
return new Promisee(function(resolve, reject){
setTimeout(function(){
resolve(1);
},100);
})
}
a().then(function(num){ ③
return new Promisee(function(resolve){
setTimeout(function(){
console.log('这是第二个setTimeout方法 已经开始执行');
resolve(num*3);
},100)
})
})
.then(function(num2){ ⑤
console.log('num2',num2);
})
</pre>
Promise.js源码中很重要的一条语句就是最后的fn(resolve) [标记6] 这条语句可以让带有参数的Promise在实例化时自动执行他的参数 同时把resolve传入 注意 这里的resolve如果是then的参数的参数 那往往是Promise的上层堆栈的resolve 这个可以从图三里很好的看出来 而最重要的所谓反转的反转的实现 就在这里! 通过handle函数 利用词法作用域 将本层堆栈的对象push到上层callbacks中 以实现不同promise的通信
[标记1]这里是我自己标记的针对不同promise做的ii属性 这样可以在函数调用时 查看它是在哪个promise调用的
[标记2]这个就有趣了 可以用来跟踪所谓通过在函数promise中添加私有属性i 在调用resolve和handle时 可以同时打印resolve和handle所在词法作用域的i属性 从而显示这两个函数是哪个
堆栈调用的^^
开始看代码(图三结合图二)
首先[标记3] a()执行返回promise[这是第一个promise] 继续调用.then 会继续返回promise[第二个promise]同时在then中的function会作为onFulfilled作为后续处理 其实就是通过第二个promise的handle方法(这个handle其实是第一个promise的) 将onFulfilled和第二个promise的resolve push到第一个promise的callbacks中(这里很重要!!!!!!做标记④ handle没有传参 所以是从上级的堆栈调用 而resolve是传参了 那直接从函数作用域调用 故是属于当前promise的resolve) 从图三的第一行可以看到
![图片描述](//img.mukewang.com/5be1148d000156a912700119.jpg)
代码往下走 到达第二个then [标记5] then返回一个promise(第三个promise) 同时then的参数同样作为onFulfilled 和 第三个promise的resolve 进入handle函数push到第二个promise中 从图三的第三四行可以看到
![图片描述](//img.mukewang.com/5be1149f0001a95608890072.jpg)
代码往下走 代码没了..
回过头 在刚刚的a()执行时 不仅返回了promise 而且通过promise的最后一行代码fn(resolve)[标记6]执行了
<pre>
function(resolve, reject){
setTimeout(function(){
resolve(1);
},100);
</pre>
这块的代码 这个函数的执行 会执行setTimeout 进行队列排队(意思就是如果有其他代码执行就先执行下面的代码 如果下面的代码有setTimeout就接着这个setTimeout排队 如果没有其他代码执行了就开始从头执行setTimeout) 然而在执行完两个then后 回过头来开始要执行这个setTimeout了 因为其他代码都执行完了 promise.js里面的不是执行代码 图一的才是 ok
好的 这个倒序整的..
现在setTimeout执行了 等待100毫秒后(这个100毫秒其实是从注册了setTimeout就开始算起的) 开始执行resolve(1); 这个resolve是第几个promise的resolove? 第一个! 因为一上来就执行"a()" 而a里面就是很单纯的将promise.js里倒数27行的resolve传到了最后的fn(resolve)里(或者说最后的fn(resolve)里的resolve就是promise.js里倒数第27行的resolve) ok? 真是 太单纯了 再也找不到这么单纯的了 好难的 555.. 从图三的第5行也可以看到
![图片描述](//img.mukewang.com/5be114c6000107fc08880040.jpg)
现在开始执行resolve了 也就是下面的这块代码 在上面的代码块中可以找到
<pre>
state = 'fulfilled';
value = newValue;
setTimeout(function setTimeoutFuc() {
callbacks.forEach(function (callback) {
console.log('pop', callbacks); // <<== 这里很有爱
handle(callback);
});
}, 0);
</pre>
状态(state)被改变成了fulfilled 然后开始setTimeout队列 问题现在其他代码都执行完了 setTimeout直接开始执行了 这里面的callbacks是第几个promise的呢 答案是第一个 因为resolve是第一个promise的
而第一个promise的resolve只能找到第一个promise里的数据 如果是第二个promise的resolve 或者handle就可以访问第二个promise或者第一个promise的数据 这是作用域的概念 ok
至于callbacks里面的数据 实际上是第一个then里面的handle push到第一个promise的callbacks里面的数据 由于在promise.js里写了"console.log('pop', callbacks);"这条语句 所以控制台的第六行显示
![图片描述](//img.mukewang.com/5be114dc00013d9108880028.jpg)
然后 handle开始处理这个callback 也就是一个对象啦
(((((((((((((((((((((((((((各单位注意 高潮开始了))))))))))))))))))))))))))))))
进入handle 现在的堆栈是第一个promise 而之前进行push时[标记4]handle用的是第一个promise的handle 因为作为then的参数中的handle也只能用第一个promise的传入啊 handle代码如下
<pre>
function handle(callback) {
if (state === 'pending') {
callbacks.push(callback);
console.log('状态是pending',"这是Promisee第",handle.caller.caller.ii,"次执行时执行的handle方法 当然 handle是在Promisee第", aa,"次定义的");//说明调用handle的是this.then中的Promisee
console.log('push', callbacks);
return;
}
//如果then中没有传递任何东西
if(!callback.onFulfilled) {
callback.resolve(value);
return;
}
console.log('状态是fulfilled','这是第',aa,'个Promisee里的handle');
var ret = callback.onFulfilled(value);
callback.resolve(ret);
}
</pre>
现在进入(第一个promise的)handle函数 由于状态(第一个promise的state)已经改为fulfilled 故直接执行callback.onFulfilled(value); value即是第一个执行的resolve传入的参数1 onFulfilled是then传入的函数参数
function(num){
return new Promisee(function(resolve){
setTimeout(function(){ ⑧
console.log('这是第二个setTimeout方法 已经开始执行');
resolve(num*3);
},100)
})
}
执行后 返回第四个promise 注意 此时会同时执行此promise的参数 可参考[标记6] 执行setTimeout方法 结果就是列入队列 此时setTimeout内的的resolve为第四个promise的resolve
下一句非常重要
`
callback.resolve(ret);
`
这个callback里的resolve是第几个promise的resolve? 看[标记4] 是第二个promise的resolve!看真相图
![图片描述](//img.mukewang.com/5be387f50001a14105060042.jpg)
而ret是第四个promise 好的 开始愉快的执行resolve吧
<pre>
function resolve(newValue) {
console.log('这是第',aa,'个Promisee里的resolve');
if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {
var then = newValue.then;
if (typeof then === 'function') {
then.call(newValue, resolve);
return; ⑦
}
}
if(newValue == undefined){
console.log('\tvalue等于undefined了 还是在resolve内执行');
if(callbacks.length == 0)
console.log('\t啥都木有');
return;
}
state = 'fulfilled';
value = newValue;
setTimeout(function setTimeoutFuc() {
callbacks.forEach(function (callback) {
console.log('pop', callbacks);
handle(callback);
});
}, 0);
}
</pre>
由于ret 也就是这里传进来的newValue是一个promise 进入第一个if 来回来去执行到了
`then.call(newValue, resolve);`
言外之意是 "我要执行第四个promise的then方法哦 而且是把第二个resolve传进去作为onFulfilled哦"
这句代码实在是太经典了 因为之后会重用第二个resolve!
好的 then执行了 返回了第五个promise!
![图片描述](//img.mukewang.com/5be38a72000185c309750023.jpg)
<pre>
this.then = function thenFun(onFulfilled) {
return new Promisee(function resolveFuc(resolve) {
handle({
onFulfilled: onFulfilled || null,
resolve: resolve
});
});
};
</pre>①②③④⑤⑥⑦⑧⑨⑩
通过第四个promise的handle(不要问我为什么是第四个 因为每个then的参数调用都只能通过作用于向上找到上级promise的handle啊 因为参数没传handle啊 ( ˇˍˇ )) 把第二个resolve(即这里的onFulfilled)和第五个resolve(第五个 wtf!)push到了第四个promise中
注意上面的[标记7] 这里有个return 按时一切的push到此结束了...
回到[标记8]的setTimeout 开始执行队列
`resolve(num*3)`
第四个resolve开始执行 通过闭包向上找到num 也就是[标记9]的value
![图片描述](//img.mukewang.com/5be38c290001d3e506450031.jpg)
此时的数字已经通过num*3 也就是1*3 得到了数字3
然后 把第四个promise中的callbacks逐个用handle处理了 callbacks里有什么 当然是上面几行里刚刚push的第二个promise的resolve和第五个promise的resolve
执行第二个promise的resolve:
`var ret = callback.onFulfilled(value);`
由于resolve里是一个setTimeout 这样开始队列
再执行下一句
`callback.resolve(ret);`
这个resolve是第五个resolve 开始执行 可是第五个promise里面啥也木有
回头开始执行队列
<pre>
setTimeout(function setTimeoutFuc() {
callbacks.forEach(function (callback) {
console.log('pop', callbacks);
handle(callback);
});
}, 0);
</pre>
第二个resolve讲第二个promise的callbacks数组 也就是代码阶段第二个then执行push的函数 调出来执行
<pre>
.then(function(num2){
console.log('num2',num2);
})
</pre>
重复的handle代码我就不再贴了
两句重要的如下
`var ret = callback.onFulfilled(value);
callback.resolve(ret);`
第一句是把
`function(num2){
console.log('num2',num2);
}`
执行 此处的value就是刚才(num*3)得到的 通过闭包获得
这样控制台打印出'num2' 3
![图片描述](//img.mukewang.com/5be390450001233806290029.jpg)
还没有结束 各位老板
还有一句没有执行完
`callback.resolve(ret)`
此时ret是console.log的返回值 即undefined 即使进入resolve也是没有意义的 因为第三个promise里没有压入如何的对象(即没有执行handle) 第三个promise是在哪生成的呢? 大家想想 答案就是执行代码的第二个then 因为then后没有then了 所以也就没有执行那个没有的then里面的handle 也就是说如果继续有then就继续handle继续push继续扯 扯 扯 扯到世界末日( ⊙ o ⊙ )啊!
![图片描述](//img.mukewang.com/5be3912e000133d306650099.jpg)
可以了 完结撒花.
网友评论