自定义promise
开始之前,先来回顾一下JS中的闭包
JS特殊的作用域
变量的作用域无非就是两种:全局变量
和局部变量
,Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量
,函数外部自然无法读取函数内的局部变量
(父对象的所有变量,对子对象都是可见的,反之则不成立)
就像下面的这个例子一样,在f2()中可以访问到f1()的n=999
,但在f1()中是获取不到f2()中的m=888
的
function f1(){
var n=999;
function f2(){
var m = 888;
alert(n); // 999
}
}
要解决在f1()中可以获取到f2()中的值或者在f1()的外部获取到f2()中的值,只要把f2()作为返回值,就可以在f1()外部读取它的内部变量了~
function f1(){
var n=999;
function f2(){
alert(n);
}
return f2;
}
var result=f1();
result(); // 999
闭包
所以!!上面说的f2()
就是闭包(闭包就是能够读取其他函数内部变量的函数
)
由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"
所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁
闭包可以用在许多地方。它的最大用处有两个:
- 一个是前面提到的
可以读取函数内部的变量
- 另一个就是
让这些变量的值始终保持在内存中
看代码理解
function f1(){
var n=999;
nAdd=function(){n+=1}
function f2(){
alert(n);
}
return f2;
}
var result=f1();
result(); // 999
nAdd();
result(); // 1000
在这段代码中,result实际上就是闭包f2()。它一共运行了两次,第一次的值是999,第二次的值是1000。这证明了,f1()中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除
原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收
f2被赋给了一个全局变量
❓❓
"nAdd=function(){n+=1}"
这一行,首先在nAdd前面没有使用var关键字,因此nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作
自定义promise整体结构
- 先创建一个lib/promise.js
// 自定义promise函数模块:IIFE
// 最外层的是匿名函数的自调用
// (function (params) {})()
(function (window) {
/**
Promise构造函数
executor:执行器构造函数(同步执行)
*/
function Promise(excutor) {
function resolve(){},
function reason(){},
// 立即同步执行excutor(执行器函数)
excutor(resolve, reject);
}
/**
* Promise原型对象的then()方法
* 指定成功/失败的回调函数
* 函数的返回值为一个新的promise对象
*/
Promise.prototype.then = function (onResolved, onRejected) {
}
/**
* Promise原型对象的catch()方法
* 指定失败的回调函数
* 函数的返回值为一个新的promise对象
*/
Promise.prototype.catch = function (onRejected) {
}
/**
* Promise函数对象的resolve方法
* 返回一个指定结果的成功的promise
*/
Promise.resolve = function (value) {
}
/**
* Promise函数对象的reject方法
* 返回一个指定reason的失败的promise
*/
//
Promise.reject = function (reason) {
}
/**
* Promise函数对象的all方法
* 返回一个promise,只有当所有的promise都成功时才成功,否则失败
*/
Promise.all = function (promises) {
}
/**
* Promise函数对象的race方法
* 返回一个promise,其结果由第一个完成的promise来决定
*/
Promise.race = function (promises) {
}
// 向外暴露的Promise函数
window.Promise = Promise;
})(window)
- 在html中引入promise.js
<body>
<script src="./lib/promise.js"></script>
<script>
const p = new Promise((resolve, reject) => {
resolve(1); // 1 代表的是 value
// reject(2); // 2 代表的是 reason
)
</script>
</body>
因为在html的promise中有resolve(value)
和 reject(reason)
,所以在promise中的executor:执行器构造函数
中也就应该有对应的参数
/**
Promise构造函数
executor:执行器构造函数(同步执行)
*/
function Promise(excutor) {
function resolve(value){},
function reason(reason){}
}
接下来稍微完善一下excutor构造函数和html文件
/**
Promise构造函数
executor:执行器构造函数(同步执行)
*/
function Promise(excutor) {
this.status = 'pending'; //给promise对象指定status属性,初始值为pending
this.data = undefined; //给promise对象指定一个用于存储结果数据的属性
this.callbacks = []; //每个元素的结构:{onResolved: {}, onRejected: {}}
function resolve(value){},
function reason(reason){}
}
<body>
<script src="./lib/promise.js"></script>
<script>
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1); // 1 代表的是 value
// reject(2); // 2 代表的是 reason
});
})
p.then(
value => {},
reason => {}
)
p.then(
value => {},
reason => {}
)
</script>
</body>
and then
如果执行html中的resolve
的时候,js中的excutor构造函数
得干嘛呢??
/**
Promise构造函数
executor:执行器构造函数(同步执行)
*/
function Promise(excutor) {
// 重定向this为_this, _this指的是Promise对象,不重定向的话this指的是window
const _this = this;
_this.status = 'pending'; //给promise对象指定status属性,初始值为pending
_this.data = undefined; //给promise对象指定一个用于存储结果数据的属性
_this.callbacks = []; //每个元素的结构:{onResolved: {}, onRejected: {}}
function resolve(value) {
// 这里判断如果当前状态不是pending,直接结束
if(_this.status !== 'pending'){
return;
}
// 将状态改为resolved
_this.status = 'resolved'
// 保存value数据
_this.data = value;
// 如果由待执行的callback函数,立即执行回调函数onResolved()
if (_this.callbacks.length > 0) {
setTimeout(() => { // 放入队列中所有成功的回调
_this.callbacks.forEach(callbacksObj => {
callbacksObj.onResolved(value);
})
}, 0);
}
}
function reject(reason) {
// 这里判断如果当前状态不是pending,直接结束
if(_this.status !== 'pending'){
return;
}
// 将状态改为rejected
_this.status = 'rejected'
// 保存value数据
_this.data = reason;
// 如果由待执行的callback函数,立即执行回调函数onResolved()
if (_this.callbacks.length > 0) {
setTimeout(() => { // 放入队列中所有失败的回调
_this.callbacks.forEach(callbacksObj => {
callbacksObj.onRejected(reason);
})
}, 0);
}
}
// html如果在执行器中抛出异常的话,promise会变成失败状态,也就是即没调用resolve又没调用reject。执行器抛出异常想让Promise变成失败状态,就需要在js中 catch error 了
// 立即同步执行excutor(执行器函数)
try {
excutor(resolve, reject);
} catch (error) { //如果执行器抛出异常,promise对象变为rejected状态
return error;
}
}
然后在.then()函数中进行操作
Promise.prototype.then = function (onResolved, onRejected) {
// 重定向this为_this, _this指的是Promise对象,不重定向的话this指的是window
const _this = this;
// 假设当前状态还是pending,将回调函数保存起来
_this.callbacks.push({
onResolved,
onRejected
})
}
⚠️ 现在写的流程是先改状态,再指定回调函数。但是正常的流程是先执行回调函数再改状态。所以这里应该修改一下。给setTimeOut 加个延迟时间。然后可以在p.then()
中打印一下
<body>
<script src="./lib/promise.js"></script>
<script>
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1); // 1 代表的是 value
// reject(2); // 2 代表的是 reason
// 这里
}, 100);
})
p.then(
value => {
console.log("onResolved1():", value);
},
reason => {
console.log("onRejected1():", reason);
}
)
p.then(
value => {
console.log("onResolved2():", value);
},
reason => {
console.log("onRejected2():", reason);
}
)
</script>
</body>
上面是resolve成功时的执行结果
还有一个就是来测试一下到底是同步调用还是异步调用
如果console.log("reject()改变状态之后");
这句先执行,p.then()
后执行,则可以说明回调函数是异步执行的
<body>
<script src="./lib/promise.js"></script>
<script>
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1); // 1 代表的是 value
// reject(2); // 2 代表的是 reason
// 用这个console.log来验证到底是同步还是异步
console.log("reject()改变状态之后");
}, 100);
})
p.then(
value => {
console.log("onResolved1():", value);
},
reason => {
console.log("onRejected1():", reason);
}
)
p.then(
value => {
console.log("onResolved2():", value);
},
reason => {
console.log("onRejected2():", reason);
}
)
</script>
</body>
怎么改状态,怎么存数据由内部定义者决定,什么时候调用由外部使用者决定
上面在.then()
中直接是假设当前状态为pending
的,但实际不一定是这样的,所以就需要在其中做状态的判断
这里先将状态定义成常量,是在(function (window) {
下面直接定义的,然后将之前在各函数中的定义的状态都改成现在这样的大写形式
(function (window) {
const PENDING = 'pending'
const RESOLVED = 'resolved'
const REJECTED = 'rejected'
/**
Promise构造函数
executor:执行器构造函数(同步执行)
*/
function Promise(excutor) {
...
修改完后在之前的.then
方法中进行状态判断
/**
* Promise原型对象的then()方法
* 指定成功/失败的回调函数
* 函数的返回值为一个新的promise对象
*/
Promise.prototype.then = function (onResolved, onRejected) {
const _this = this;
// 假设当前状态还是PENDiNG,将回调函数保存起来
// 这里的this指的是promise对象
if(_this.status === 'PENDING'){
_this.callbacks.push({
onResolved,
onRejected
})
}else if (_this.status === 'RESOLVED'){
setTimeout(() => {
onResolved(_this.data);
});
}else { // rejected
setTimeout(() => {
onRejected(_this.data);
});
}
}
就像.then()
函数上面的注释里面说的一样,最后结果是返回一个新的promise对象,所以这里应该return new Promise
👇
// 返回一个新的promise对象
return new Promise ((resolve, reject) => {
})
又因为这个Promise的参数中有 resolve 和 reject,如果你想在执行完函数之后根据结果去判断到底是调用resolve还是reject,这个时候就需要用上面的这个return语句将上面的状态判断包起来,然后在里面进行判断
/**
* Promise原型对象的then()方法
* 指定成功/失败的回调函数
* 函数的返回值为一个新的promise对象
*/
Promise.prototype.then = function (onResolved, onRejected) {
const _this = this;
// 假设当前状态还是PENDiNG,将回调函数保存起来
// 这里的this指的是promise对象
// 返回一个新的promise对象
return new Promise((resolve, reject) => {
if (_this.status === 'PENDING') {
_this.callbacks.push({
onResolved,
onRejected
})
} else if (_this.status === 'RESOLVED') {
setTimeout(() => {
/**
* 1. 如果抛出异常,return的Promise就会失败,reason就是error
* 2. 如果回调函数返回的不是Promise,return的promise就会成功,value就是返回的值
* 3. 如果回调函数返回的是Promise,return的promise的结果就是这个promise的结果
*
*/
try {
const result = onResolved(_this.data);
if (result instanceof Promise) {
// 3. 如果回调函数返回的是Promise,return的promise的结果就是这个promise的结果
result.then(
value => resolve(value), // 当result成功时,让return 的 Promise 也成功
reason => reject(reason) // 当result失败时,让return 的 Promise 也失败
// 与下面写法一致
// value => {
// resolve(value);
// },
// reason => {
// reject(reason);
// }
)
} else {
// 2. 如果回调函数返回的不是Promise,return的promise就会成功,value就是返回的值
resolve(result);
}
} catch (error) {
// 1. 如果抛出异常,return的Promise就会失败,reason就是error
reject(error);
}
});
} else { // rejected
setTimeout(() => {
onRejected(_this.data);
});
}
})
}
接下来将上面else if 中的的写法更改成更简洁版
setTimeout(() => {
/**
* 1. 如果抛出异常,return的Promise就会失败,reason就是error
* 2. 如果回调函数返回的不是Promise,return的promise就会成功,value就是返回的值
* 3. 如果回调函数返回的是Promise,return的promise的结果就是这个promise的结果
*
*/
try {
const result = onResolved(_this.data);
if (result instanceof Promise) {
// 3. 如果回调函数返回的是Promise,return的promise的结果就是这个promise的结果
// result.then(
// value => resolve(value), // 当result成功时,让return 的 Promise 也成功
// reason => reject(reason) // 当result失败时,让return 的 Promise 也失败
// )
// 上面的写法和下面的是一样的
// 说明
// 假设result成功了,那它会调 resolve,并且给resolve传入成功的value
// 那return 的 Promise 就会变成成功的状态
result.then(resolve, reject);
} else {
// 2. 如果回调函数返回的不是Promise,return的promise就会成功,value就是返回的值
resolve(result);
}
} catch (error) {
// 1. 如果抛出异常,return的Promise就会失败,reason就是error
reject(error);
}
});
else if 中可以这样写的话,那么else 中也就可以这样写了,只是把之前的 onResolved() 改为 onRejected()
else { // rejected
setTimeout(() => {
/**
* 1. 如果抛出异常,return的Promise就会失败,reason就是error
* 2. 如果回调函数返回的不是Promise,return的promise就会成功,value就是返回的值
* 3. 如果回调函数返回的是Promise,return的promise的结果就是这个promise的结果
*
*/
try {
const result = onRejected(_this.data);
if (result instanceof Promise) {
// 3. 如果回调函数返回的是Promise,return的promise的结果就是这个promise的结果
// result.then(
// value => resolve(value), // 当result成功时,让return 的 Promise 也成功
// reason => reject(reason) // 当result失败时,让return 的 Promise 也失败
// )
// 上面的写法和下面的是一样的
// 说明
// 假设result成功了,那它会调 resolve,并且给resolve传入成功的value
// 那return 的 Promise 就会变成成功的状态
result.then(resolve, reject);
} else {
// 2. 如果回调函数返回的不是Promise,return的promise就会成功,value就是返回的值
resolve(result);
}
} catch (error) {
// 1. 如果抛出异常,return的Promise就会失败,reason就是error
reject(error);
}
});
}
接下来
看一下.then函数里面的代码
如果当前是pending
状态的时候,就直接_this.callbacks.push 存起来,而且如果成功了的话就会执行onResolved
函数,如果失败了就执行onRejected
。但问题是执行完它们之后,怎么影响这个Promise
的状态呢??(可以看出直接将onResolved
,onRejected
存进callbacks
中是不行的
Promise.prototype.then = function (onResolved, onRejected) {
const _this = this;
// 假设当前状态还是PENDiNG,将回调函数保存起来
// 这里的this指的是promise对象
// 返回一个新的promise对象
return new Promise((resolve, reject) => {
if (_this.status === 'PENDING') {
_this.callbacks.push({
onResolved,
onRejected
})
} else if (_this.status === 'RESOLVED') {
setTimeout(() => {
/**
* 1. 如果抛出异常,return的Promise就会失败,reason就是error
* 2. 如果回调函数返回的不是Promise,return的promise就会成功,value就是返回的值
* 3. 如果回调函数返回的是Promise,return的promise的结果就是这个promise的结果
*
*/
try {
const result = onResolved(_this.data);
if (result instanceof Promise) {
// 3. 如果回调函数返回的是Promise,return的promise的结果就是这个promise的结果
// result.then(
// value => resolve(value), // 当result成功时,让return 的 Promise 也成功
// reason => reject(reason) // 当result失败时,让return 的 Promise 也失败
// )
// 上面的写法和下面的是一样的
// 说明
// 假设result成功了,那它会调 resolve,并且给resolve传入成功的value
// 那return 的 Promise 就会变成成功的状态
result.then(resolve, reject);
} else {
// 2. 如果回调函数返回的不是Promise,return的promise就会成功,value就是返回的值
resolve(result);
}
} catch (error) {
// 1. 如果抛出异常,return的Promise就会失败,reason就是error
reject(error);
}
});
} else { // rejected
setTimeout(() => {
/**
* 1. 如果抛出异常,return的Promise就会失败,reason就是error
* 2. 如果回调函数返回的不是Promise,return的promise就会成功,value就是返回的值
* 3. 如果回调函数返回的是Promise,return的promise的结果就是这个promise的结果
*
*/
try {
const result = onRejected(_this.data);
if (result instanceof Promise) {
// 3. 如果回调函数返回的是Promise,return的promise的结果就是这个promise的结果
// result.then(
// value => resolve(value), // 当result成功时,让return 的 Promise 也成功
// reason => reject(reason) // 当result失败时,让return 的 Promise 也失败
// )
// 上面的写法和下面的是一样的
// 说明
// 假设result成功了,那它会调 resolve,并且给resolve传入成功的value
// 那return 的 Promise 就会变成成功的状态
result.then(resolve, reject);
} else {
// 2. 如果回调函数返回的不是Promise,return的promise就会成功,value就是返回的值
resolve(result);
}
} catch (error) {
// 1. 如果抛出异常,return的Promise就会失败,reason就是error
reject(error);
}
});
}
})
}
这块还没有完善好…
自定义resolve/reject