加载图片,用promise
function loadURL (url) {
let img = new Image();
return new Promise((resolve,reject) => {
img.onload = function () {
resolve(img);
/11111
}
img.onerror = function () {
reject();
throw `${url} is nor correct`
}
img.src = url
})
}
resolve(img) 有两个作用, 一个是 改变状态,声明状态
另一个是将返回值传递出去.
promise细致用法
let p = new Promise((res,rej) => {
res();
rej();
})
可以看出
- promise 状态是不可逆的.
let p = new Promise((res,rej) => {
res('suc');
rej('err');
})
let p1 = p.then((data) => {console.log(data,'p');},(data) => {console.log(data,'p');});
p1.then((data) => {console.log(data,'p1');},(data) => {console.log(data,'p1');})
原来如此, 我还在想呢, 如果每次都要 new Promise, 那then 的链式调用也没太大意思
想要链式调用,要解决两个东西, 一个是状态的改变, 一个是数据的传递.
then里执行过后, 默认下个then状态都会变成 resolve
猜测, p 的两个回调 ,如果执行了第一个, 就会触发 p1 的第一个回调
- then方法会返回promise对象, 对应回调函数中的return 作为Promise对象中的data
let p = new Promise((res,rej) => {
res('suc');
rej('err');
})
let p1 = p.then((data) => {console.log(data,'p');return "sucsuc"},(data) => {console.log(data,'p');return "errerr"});
p1.then((data) => {console.log(data,'p1');},(data) => {console.log(data,'p1');})
Promise.resolve()
返回一个带有明确data且状态为 rosolved 的 Promise对象.
let p = Promise.resolve(123);
p.then((data) => {console.log(data);})/123
Promise.reject()
返回一个带有明确data且状态为 rejected的 Promise对象.
let p = Promise.reject(123);
p.then(()=>{},(data) => {console.log(data);})/123
Promise.all([p1,p2,p3])
let p1 = Promise.resolve(111);
let p2 = new Promise((res,rej) => {
setTimeout(function () {
res(222)
},1000)
})
let p3 = new Promise((res,rej) => {
setTimeout(function () {
res(333)
},2000)
})
let P = Promise.all([p1,p2,p3]);
P.then((data) => {console.log(data);},(data) => {console.log(data);})
只有当三个全为 resolved 时, 才会触发
执行时间取决于最后一个 执行resolve的promise对象
let p1 = Promise.resolve(111);
let p2 = new Promise((res,rej) => {
setTimeout(function () {
rej(222)
},1000)
})
let p3 = new Promise((res,rej) => {
setTimeout(function () {
rej(333)
},2000)
})
let P = Promise.all([p1,p2,p3]);
P.then((data) => {console.log(data);},(data) => {console.log(data);})
可知, 只要有一个 触发 reject 就会触发,并且只返回第一个reject的p的数据
执行时间取决于第一个触发 reject的promise对象.
Promise.all() 的用处
比如, 我要请求三次数据, 我可以每次获取数据都渲染一次页面,(3次)
也可以三个数据都获得之后 再渲染一次页面(1次)
但只要有一个数据是无法获取, 就要渲染一个 error提示页面
这个时候用 Promise.all() 就很适合.
类似的, 对于管理多个并行异步请求, 似乎是比较适合的.
并且这几个请求缺一不可.
这里的并行异步请求 指的是 各个请求之间没有顺序上的依赖.
如果存在顺序上的依赖, 明显用then 比较合适.
Promise.race([p1,p2,p3])
let p1 = new Promise((res,rej) => {
setTimeout(function () {
res(111)
},3000)
})
let p2 = new Promise((res,rej) => {
setTimeout(function () {
rej(222)
},1000)
})
let p3 = new Promise((res,rej) => {
setTimeout(function () {
rej(333)
},2000)
})
let P = Promise.race([p1,p2,p3]);
P.then((data) => {console.log(data);},(data) => {console.log(data);})
可知
Promise.race() 求的是三个请求中最快的那个,
不关心 请求的结果如何.(这样就没什么太大意思啊)
通常用于 网络测试?
Promise.prototype.catch()
Promise.resolve(1).catch((data) => {console.log(data)});/ 没有触发
Promise.reject(1).catch((data) => {console.log(data)}); / 返回1
catch()是 用来代替 p.then(null,(data)=>{}) 的?
new Promise((res,rej) => {
rej('err')
}).then((data) => {console.log(data)}).catch((data) => {console.log(data)})/ err
new Promise((res,rej) => {
rej('err')
}).then((data) => {console.log(data)},data => {
console.log(123);
return data;
}).catch((data) => {console.log(data)})
表明执行了 then(null,()=>{})之后, 就不会再执行catch 了?
为什么不看源码?
因为看不懂
代码的28法则
一个完整的代码总是包含很多辅助性的代码
这些辅助性的代码的作用是 增强代码的稳定性,健壮性,安全性
这些辅助性的代码 会让代码变得很难阅读
核心代码占据 20% 辅助性代码占据80%
模拟封装promise
step 1 模拟参数校验, 以及基本架构
class myPromise {
constructor (fn) {
if (typeof fn !== "function") {
throw TypeError(`myPromise resolver ${fn} is not a function`)
}
this.state = "pending";
this.data = undefined;
let resolve = (data) => {
this.state = "resolved"
this.data = data;
}
let reject = (data) => {
this.state = "rejected"
this.data = data;
}
fn(resolve,reject);
}
then () {
}
static resolve () {
}
static reject () {
}
}
new myPromise(123);
step2 模拟状态不可逆
let resolve = (data) => {
if (this.state == "pending") {
this.state = "resolved"
this.data = data;
}
}
let reject = (data) => {
if (this.state == "pending") {
this.state = "rejected"
this.data = data;
}
}
step3 模拟 Promise.resolve,Promise.reject
static resolve (data) {
return new myPromise((suc) => {
suc(data);
})
}
static reject (data) {
return new myPromise((suc,err) => {
err(data);
})
}
step4 模拟 then
then (resolveFn, rejectFn) {
if (this.state == "resolved") {
resolveFn(this.data);
}
if (this.state == "rejected") {
rejectFn(this.data);
}
}
step5 判断回调的返回值是否为 promise 对象
then (resolveFn, rejectFn) {
if (this.state == "resolved") {
let rus = resolveFn(this.data);
if (rus instanceof myPromise) {
console.log('is a promise');
}else{
console.log(" not a promise");
}
}
if (this.state == "rejected") {
rejectFn(this.data);
}
}
step6 根据判断 返回不同的 promise 对象,
这里是稍微难的, 感觉很巧妙.不容易想到.
then (resolveFn, rejectFn) {
if (this.state == "resolved") {
let rus = resolveFn(this.data);
if (rus instanceof myPromise) {
// 返回新 promise 对象 状态 数据都由 new时定义
return rus
}else{
// 也是返回新promise 对象, 但状态确定为 resolved, 数据则为 rus, 这个数据储存在这个新对象的 this.data中, 便于这个对象的then调用
return myPromise.resolve(rus);
}
}
if (this.state == "rejected") {
let rus = rejectFn(this.data);
if (rus instanceof myPromise) {
return rus
}else{
return myPromise.resolve(rus);
}
}
}
到此时,先贴一下整体代码
class myPromise {
constructor (fn) {
if (typeof fn !== "function") {
throw TypeError(`myPromise resolver ${fn} is not a function`)
}
this.state = "pending";
this.data = undefined;
let resolve = (data) => {
if (this.state == "pending") {
this.state = "resolved"
this.data = data;
}
}
let reject = (data) => {
if (this.state == "pending") {
this.state = "rejected"
this.data = data;
}
}
fn(resolve,reject);
}
then (resolveFn, rejectFn) {
if (this.state == "resolved") {
let rus = resolveFn(this.data);
if (rus instanceof myPromise) {
return rus
}else{
return myPromise.resolve(rus);
}
}
if (this.state == "rejected") {
let rus = rejectFn(this.data);
if (rus instanceof myPromise) {
return rus
}else{
return myPromise.resolve(rus);
}
}
}
static resolve (data) {
return new myPromise((suc) => {
suc(data);
})
}
static reject (data) {
return new myPromise((suc,err) => {
err(data);
})
}
}
不过无法解决 异步的问题.
作业1, 用 原生写一遍以上代码
function myPromise(fn) {
if(typeof fn !== "function") {
throw TypeError(`${fn} is not a function`)
}
this.state = "pending";
this.data = undefined;
// 这个地方不用箭头函数, 就需要把 this用变量存一下
let _this = this;
let resolve = function(data) {
if(_this.state == "pending") {
_this.state = "resolved";
_this.data = data;
}
}
let reject = function(data) {
if(_this.state == "pending") {
_this.state = "rejected";
_this.data = data;
}
}
fn(resolve, reject);
}
myPromise.resolve = function(data) {
return new myPromise((res) => {
res(data);
})
}
myPromise.reject = function(data) {
return new myPromise((res,rej) => {
rej(data);
})
}
myPromise.prototype.then = function(resolveFn, rejectFn) {
if (this.state == "resolved") {
let rus = resolveFn(this.data);
if (rus instanceof myPromise) {
return rus
}else{
return myPromise.resolve(rus);
}
}
if (this.state == "rejected") {
let rus = rejectFn(this.data);
if (rus instanceof myPromise) {
return rus
}else {
return myPromise.reject(rus);
}
}
}
作业2 思考一下如何解决异步问题,
也就是 state == 'pending'时该怎么办?
什么时候resolve 执行, 改变了状态, 什么时候就执行 then 里面的回调函数.
也就是说, then里的函数,应该是先进行注册,进行保存,
当 状态发生变化时, 相应保存的函数再执行.
并且执行完之后,再返回 promise对象.
then是链式调用,每个then不能马上返回promise对象,
那样会出问题.
所以关键是要存这个函数. 然后再考虑怎么返回promise对象,
因为还要把注册的不同的函数, 分配给不同的promise 对象?
step1
class myPromise {
constructor(fn) {
if(typeof fn !== "function") {
throw TypeError(`myPromise resolver ${fn} is not a function`)
}
this.state = "pending";
this.data = undefined;
// 用来储存 resolveFn的数组
this.resList = [];
// 用来储存 rejectFn的数组
this.rejList = [];
let resolve = (data) => {
if(this.state == "pending") {
this.state = "resolved"
this.data = data;
if (this.resList.length > 0) {
this.resList.shift()();
}
}
}
let reject = (data) => {
if(this.state == "pending") {
this.state = "rejected"
this.data = data;
if (this.rejList.length > 0) {
this.rejList.shift()();
}
}
}
fn(resolve, reject);
}
then(resolveFn, rejectFn) {
this.resList.push( () => {
let rus = resolveFn(this.data);
if(rus instanceof myPromise) {
return rus
} else {
return myPromise.resolve(rus);
}
})
this.rejList.push(() => {
let rus = rejectFn(this.data);
if(rus instanceof myPromise) {
return rus
} else {
return myPromise.reject(rus);
}
})
}
static resolve(data) {
return new myPromise((suc) => {
suc(data);
})
}
static reject(data) {
return new myPromise((suc, err) => {
err(data);
})
}
}
let p = new myPromise((suc, err) => {
setTimeout(function () {
err('suc');
},2000);
});
p.then(null, (data) => console.log(data))
这样处理,能够完成第一步的异步,
但一旦链式调用, 则会报错
p.then(null, (data) => console.log(data)).then(null,data => console.log(data))
这很正常, 因为 我们的then在执行第一次后没有返回任何东西.
我们应该返回一个临时promise对象?,你不能返回跟原来的一样的,
这个promise对象只需要做一件事情, 就是把 then里的回调都注册进来.
问题是, 我们是不是应该把所有的回调都储存在同一个数组?
应该, 因为我们还要分出去, 所以这个数组不能放在实例上,
而应该放在原型上, 这样每个promise对象,都可以注册函数.
所以问题变成了,
state=='pending' 时, 我们要返回什么promise对象?
这个对象并不需要设置 resolve,reject, 状态,
假设我们随便返回一个promise对象,
在原型的数组上注册了相应的函数,
但我们怎么去寻找呢?
应该是每个promise对象的 resolve/reject 对应相应的注册的函数.
根据对象的什么进行查找呢? 根据顺序嘛?
但这个对象和函数之间有可能不是一对一的关系, 而是一对多的关系.
所以这个数组应该是个二维数组?
假设我们在 state == 'pending'时, 返回了一个对象,
然后没有在原型上的数组中进行注册,而是在这个实例上进行了注册.
那我们要做的就是在then执行之后返回对象时,或者传递数据的时候, 只要在这个对象上操作就可以了?
怎么确定下一个promise对象是哪一个?
第一个思路是还是在 原型上定义一个数组,专门用来存放生成的promise对象,
然后按顺序执行即可.
但这么做似乎不妥,不妥在于,同一个promise对象可以并行执行多个then(),
返回的promise对象,应该都是不同的, 那这些对象是放在原型的同一个数组嘛?
第二个思路是,链表的思路,
我只需要在这个实例上标记好,我要返回的下一个实例是谁就可以了.
我们试一下? 虽然还是一团乱麻, 但可以试一下,
对了还有一个问题,
这个问题是, 按照上面这个逻辑讲,
实例对象应该在 then() 的时候先产生, 然后在resolve的时候返回.
可如果是同步代码,这就会出问题.
同步代码是, resolve 会比then先进行读取,因为,我们一般会把then写在后面.
解决方法是, 我们用 setTimeout() 让 resolve 运行缓一下?
一敲代码就发现问题了,
上面的思路行不通,
行不通的原因是,
说到底, 我是想在then时先创建对象, 然后在resolve时 返回对象.
问题在于, 数据必须在创建对象的时候传递,
而数据只有在resolve的时候才会得到.
也就是说 then时我生成了对象,但无法把数据传进去
不对,严格来讲还是可以改变data的, 只不过方法稍微暴力了一点.
不对是非常的暴力, 感觉非常不可取, 但我们先写一下,,
class myPromise {
constructor(fn) {
if(typeof fn !== "function") {
throw TypeError(`myPromise resolver ${fn} is not a function`)
}
this.state = "pending";
this.data = undefined;
用来存放注册的函数
this.resList = [];
this.rejList = [];
let resolve = (data) => {
if(this.state == "pending") {
this.state = "resolved"
this.data = data;
如果有注册的函数就执行注册的函数, 注意,这里要用传参的方式把数据传进去
this.resList.forEach((item) => {
item(this.data);
})
}
}
let reject = (data) => {
if(this.state == "pending") {
this.state = "rejected"
this.data = data;
如果有注册的函数就执行注册的函数, 注意,这里要用传参的方式把数据传进去
this.rejList.forEach((item) => {
item(this.data);
})
}
}
这里是为了能够让 then在resolve,reject之前执行.
主要是解决同步时候的问题.
setTimeout(function () {
fn(resolve, reject);
},0)
}
then(resolveFn, rejectFn) {
把函数放进相应的数组里.
this.resList.push( (data) => {
这里不能用 this.data 因为我们会有一次更换对象的操作.
let rus = resolveFn(data);
if(rus instanceof myPromise) {
从这里开始就惨不忍睹了, 非常暴力
把之前生成的对象里的 注册函数 暴力赋值
rus.resList = this.next.resList;
rus.next = this.next.next;
return rus
} else {
用暴力的赋值的方式更改对象的状态和 数据
this.next.state = "resolved";
this.next.data = rus;
this.next.resList.forEach((item) => {
item(rus);
})
}
})
this.rejList.push((data) => {
let rus = rejectFn(data);
if(rus instanceof myPromise) {
rus.rejList = this.next.rejList;
rus.next = this.next.next;
return rus
} else {
this.next.state = "rejected";
this.next.data = rus;
this.next.rejList.forEach((item) => {
item(rus);
})
}
})
这里我们先生成了一个promise对象, 并且返回了出去.
this.next = new myPromise((resolve,reject) => {});
return this.next;
}
static resolve(data) {
return new myPromise((suc) => {
suc(data);
})
}
static reject(data) {
return new myPromise((suc, err) => {
err(data);
})
}
}
let p = new myPromise((suc, err) => {
setTimeout(function () {
err('suc');
},2000);
});
p.then(null, (data) => {console.log(data);
return new myPromise((res,rej) => {
setTimeout(function () {
rej('err');
},1000)
})
}).then(null,data => {console.log(data);return 223})
.then(null,data => console.log(data))
貌似整个能够模拟今天所学的内容.
但异常暴力,感觉问题很大很大..
不过思考两个小时,能够坚持思考,并敲一敲,还是挺欣慰的.
明天再看视频.