Promise怎么学?
我们知道Promise是ES6新增加的用来解决异步问题的,那么我们当了解到这个新事物之后我们改如何去学习他呢?
根据它的用法new Promise,我们知道Promise肯定是一个构造函数,那么构造函数上面肯定会有自己的静态方法,也会在原型上拓展自己的方法,以供自己的实例调用,那么我们就可以在浏览器的控制台打印一下,看看我们需要学习它的方法有哪些?
打印Promise这个函数对象(函数分三种角色:普通函数、构造函数、对象---此知识点会在后续的文章中发布):
打印Promise的原型:
打印Promise的实例:new Promise(()=>{}):
这里强调一下:new Promise的时候必选要给他传递一个回调函数:否则会报错!,
我们可以看到,当我们实例化Promise后他的实例上会有两个属性,
PromiseStatus:状态,
PromiseValue:值。(下面会讲)
Promise基基本使用方法:
由一个面试题引出Promise的基本用法:
设计一个等待函数,等待n时间后执行要做的事情
function delay(callback,interval = 1000){
return new Promise((resolve)=>{
let timer = setTimeout(()=>{
clearTimeout(timer);
timer=null;
resolve()
})
},interval)
}
delay(1000).then(()=>{
console.log(1);
return delay(2000);
}).then(()=>{
console.log(2);
return delay(3000);
}).then(()=>{
console.log(3)
})
- resolve/reject:传递给executor的参数,(参数值都是函数)
- promise的初始状态是"pedding", 初始值是undefined。
- resolve([value]) 修改promise状态为resolved/fulfilled成功状态,并且改变其值为[value],
- reject相反
Promise对象上的方法及使用
我们在上图中可以看到在Promise函数对象上有好多的属性方法可以供我们使用,我们最常用的有resolve和reject以及all和race方法。
那么我们看看他们的使用方法:
【resolve】:很简单,执行它时:Promise.resolve([value]),会返回一个成功态(fulfilled)的Promise实例,并且promise的PromiseValue会被改变为:value;
【reject】: 执行它时:Promise.reject([reason]),会返回一个失败态(rejected)的Promise实例,并且promise的PromiseValue会被改变为:reason;
all: 我们需要给他传递一个每一项是Promise的数组,返回出的结果是一个Promise实例,通过.then方法可以接到返回出的值,也是一个数组,并且数组中每一项都是传入的Promise实例的输出结果。
【需要注意1。如果数组中有常量项,那么返回的结果中会原值显示这一项】
【需要注意2,数组中只要有一个promise实例返回的结果是失败的整个就会失败,失败的原因就是这个实例失败的原因】
race: 传入的参数也是一个promise的数组,执行时,只要有一个Promise变为成功态就会返回结果;
Promise原型上的方法及使用
then: 这个方法会返回一个新的Promise实例,并且这个实例的状态有一下集中情况决定:
.catch: catch====then(null,function(){});
其实catch是由then方法的一个特性:顺延性诞生的;
【then方法的顺延性】:当我们在执行then后,如果在第一个promise中是返回的rejected状态的实例,但是在then方法里面没有第二个回调函数也就是null,那么这个错误的原因不会打印,因为没有承载它的回调函数,如果在.then.then方法里面有第二个回调函数,那么会在这个回调函数中拿到错误的结果。当然确实第一个参数函数也是如此;因此catch也就是在then的后面接收错误的,p1.then(()=>{},null).then(()=>{},null).catch(/在这里捕获到错误/)
Promise源码手写:
基本使用:
function feiPromise(executor) {
let _this = this;//保存this
if (typeof executor !== 'function') {
throw new TypeError('MyPromise resolver ' + executor + ' is not a function'); }
this.PromiseValue = null;//保存值
this.PromiseStatus = 'pedding';//保存状态
//改变状态和值,并执行then中的参数函数;
function change(status, value) {
if (_this.PromiseStatus !== 'pedding') return;//状态一经改变就不允许再改变
_this.PromiseStatus = status;
_this.PromiseValue = value;
}
//用try catch 捕获立即执行函数executor执行中的错误,有错误的话直接reject抛出。
try {
executor(function (value) {
change('fulfilled', value);
}, function (reason) {
change('rejected', reason);
}) } catch (error) {
change('rejected', error.message);
}}
//测试调用:====>
//首先不调用改变状态的方法
let p1 = new Promise((resolve,reject)=>{});Ï
console.log(p1)//->>feiPromise {PromiseValue: null, PromiseStatus: "pedding", resolveFunc: ƒ, rejectFunc: ƒ}
//调用改变状态的方法:
let p1 = new Promise((resolve,reject)=>{resolve(111});Ïconsole.log(p1)//->>feiPromise {PromiseValue: 888, PromiseStatus: "fulfilled", resolveFunc: ƒ, rejectFunc: ƒ}
补充方法:
resolve和reject方法:
feiPromise.resolve = function (value) {
return new feiPromise((resolve) => { resolve(value) })//直接将改变状态后的实例返回
}
feiPromise.reject = function (reason) {
return new feiPromise((_, reject) => { reject(reason) })//直接将改变状态后的实例返回
}
.then方法:
我们注意到当我们执行then的时候我们是可以接到通过resolve([value])或reject([reason])方法改变的PromiseValue的值的,所以我们在内部实现的时候:当我们改变状态和值得时候也就是执行change方法的时候我们确保then方法里面的回调函数是已经注册好的,这样我们在可以执行其中的回调函数,这样在then的回调函数里面我们才可以拿到对应的值:因此实现这个功能我们采用定时器,将执行then回调函数的方法写在定时器里面。
因次我们修改change方法:
function feiPromise(executor) { let _this = this;
//参数校验 if (typeof executor !== 'function') {
throw new TypeError('MyPromise resolver ' + executor + ' is not a function');
}
// 设置实例的私有属性
this.PromiseValue = null;//保存值
this.PromiseStatus = 'pedding';//保存状态
this.resolveFunc = function () { };//保存then回调函数的第一个参数
this.rejectFunc = function () { };//保存then回调函数的第二个参数
// 修改实例的状态和value:只有当前状态为pending才能修改状态
function change(status, value) {
if (_this.PromiseStatus !== 'pedding') return;
_this.PromiseStatus = status;
_this.PromiseValue = value;
// 通知基于.then注入的某个方法执行(异步的)
var promiseTimer = setTimeout(() => {//定时器保证调用时,then中的回调函数已经注册好
clearTimeout(promiseTimer);
promiseTimer = null;
//执行对应的方法,并把参数传递PromiseValue
_this.PromiseStatus == 'fulfilled' ? _this.resolveFunc.call(_this, _this.PromiseValue) : _this.rejectFunc.call(_this, _this.PromiseValue) }, 0); } try { executor(function (value) { change('fulfilled', value); }, function (reason) { change('rejected', reason); }) } catch (error) { change('rejected', error.message); }}
feiPromise.prototype.then = function (resolveFunc, rejectFunc) {
//将对应的方法存储到实例上
this.resolveFunc = resolveFunc;
this.rejectFunc = rejectFunc;
}
then的特性【1】:顺延机制,既然没有如果没有对应的回调函数的话它会顺延到下面的then,
那么想要实现我们就可以在then的内部,检测是否传递了两个参数,没有传递的话我们就默认给他赋值一个对应的Promise;
function feiPromise(executor) {
let _this = this;
//参数校验
if (typeof executor !== 'function') {
throw new TypeError('MyPromise resolver ' + executor + ' is not a function');
}
// 设置实例的私有属性
this.PromiseValue = null;//保存值
this.PromiseStatus = 'pedding';//保存状态
this.resolveFunc = function () { };//保存then回调函数的第一个参数
this.rejectFunc = function () { };//保存then回调函数的第二个参数
// 修改实例的状态和value:只有当前状态为pending才能修改状态
function change(status, value) {
if (_this.PromiseStatus !== 'pedding') return;
_this.PromiseStatus = status;
_this.PromiseValue = value;
// 通知基于.then注入的某个方法执行(异步的)
var promiseTimer = setTimeout(() => {//定时器保证调用时,then中的回调函数已经注册好
clearTimeout(promiseTimer);
promiseTimer = null;
//执行对应的方法,并把参数传递PromiseValue
_this.PromiseStatus == 'fulfilled' ? _this.resolveFunc.call(_this, _this.PromiseValue) : _this.rejectFunc.call(_this, _this.PromiseValue)
}, 0);
}
try {
executor(function (value) {
change('fulfilled', value);
}, function (reason) {
change('rejected', reason);
})
} catch (error) {
change('rejected', error.message);
}}
feiPromise.prototype.then = function (resolveFunc, rejectFunc) {
//如果没有传递参数
if (typeof resolveFunc !== 'function') { resolveFunc = function (value) { return feiPromise.resolve(value) } } if (typeof rejectFunc !== 'function') { rejectFunc = function (reason) { return feiPromise.reject(reason); } }
特性【2】:then会返回一个新的promise实例,这个实例的状态和值有三种情况:
function feiPromise(executor) {
let _this = this;
//参数校验
if (typeof executor !== 'function') {
throw new TypeError('MyPromise resolver ' + executor + ' is not a function');
}
// 设置实例的私有属性
this.PromiseValue = null;//保存值
this.PromiseStatus = 'pedding';//保存状态
this.resolveFunc = function () { };//保存then回调函数的第一个参数
this.rejectFunc = function () { };//保存then回调函数的第二个参数
// 修改实例的状态和value:只有当前状态为pending才能修改状态
function change(status, value) {
if (_this.PromiseStatus !== 'pedding') return;
_this.PromiseStatus = status;
_this.PromiseValue = value;
// 通知基于.then注入的某个方法执行(异步的)
var promiseTimer = setTimeout(() => {//定时器保证调用时,then中的回调函数已经注册好
clearTimeout(promiseTimer);
promiseTimer = null;
//执行对应的方法,并把参数传递PromiseValue
_this.PromiseStatus == 'fulfilled' ? _this.resolveFunc.call(_this, _this.PromiseValue) : _this.rejectFunc.call(_this, _this.PromiseValue)
}, 0);
}
try {
executor(function (value) {
change('fulfilled', value);
}, function (reason) {
change('rejected', reason);
})
} catch (error) {
change('rejected', error.message);
}}
feiPromise.prototype.then = function (resolveFunc, rejectFunc) {
if (typeof resolveFunc !== 'function') {
resolveFunc = function (value) {
return feiPromise.resolve(value)
} }
if (typeof rejectFunc !== 'function') {
rejectFunc = function (reason) {
return feiPromise.reject(reason);
} }
return new feiPromise((resolve, reject) => {
this.resolveFunc = function (value) {
try {
var x = resolveFunc(value);
x instanceof feiPromise ? x.then(resolve, reject) : resolve(x);//x.then(resolve, reject) :其实是相当于又执行了一次.then,然后走的三目的第二个条件
} catch (error) {
reject(error);
}
};
this.rejectFunc = function (reason) {
try {
var x = rejectFunc(reason);
x instanceof feiPromise ? x.then(resolve, reject) : resolve(x);//x.then(resolve, reject) :其实是相当于又执行了一次.then,然后走的三目的第二个条件
} catch (error) {
reject(error);
}
} })
all方法实现:
feiPromise.all = function (promiseArr) {
var i, len = promiseArr.length, values = [], index = 0;
return new feiPromise((resolve, reject) => {
for (; i < len; i++) {
(function (i) {
var item = promiseArr[i];
!(item instanceof feiPromise) ? feiPromise.resolve(item) : null;
item.then((value) => {
index++;
values[i] = value;
if (index >= len) {
resolve(values)
}
}).catch((reason) => { reject(reason) })
})(i)
} })}