加载图片,用promise
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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细致用法

1
2
3
4
5
let p = new Promise((res,rej) => {
res();
rej();
})

可以看出
- promise 状态是不可逆的. ``` let p = new Promise((res,rej) => { res(‘suc’); rej(‘err’); })
1
2
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 的链式调用也没太大意思 > 想要链式调用,要解决两个东西, 一个是状态的改变, 一个是数据的传递. > 状态似乎是启用哪个回调,就会默认是相应的状态 > 猜测, p 的两个回调 ,如果执行了第一个, 就会触发 p1 的第一个回调 > * then方法会返回promise对象, 对应回调函数中的return 作为Promise对象中的data
1
2
3
4
5
6
7
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');})

如此我们可以理解为 当第一次new Promise() 执行时, 返回的状态, 会决定后序的链全是 resolve,或者是 reject? 不会出现中途从resolve 转到 reject? 除非 中间重新new?
整理一下
- Promise 对象的状态的是不可逆的
- P.then()执行完后会返回一个promise对象, 我们称为 PP吧
- 如果P.then() 的回调函数返回的是非 promise 对象, PP的状态由回调函数是哪个来决定, PP.then()的回调函数的参数就是 P的回调函数返回的值 如果P.then() 回调函数返回的是promise对象, 也就是在P.then() 的回调函数中new 了一个 Promise() PP的状态和数据就由 new 时来重新决定.
- 这整理的简直不想看第二遍…呵呵.. 也许应该画图. 或者看代码.

如果回调中 又new了一个 则状态又由new时 的resolve/reject 来决定 错了错了, 画了半天的图, 都是错的. 默认 then()执行过后状态都会变成resolve, 也就是说,刚开始进入了 reject回调, 但执行过后 下一个promise的对象还是resolved 回头还是删了这个图吧.
Promise.resolve() 返回一个带有明确data且状态为 rosolved 的 Promise对象.
1 2let p = Promise.resolve(123); p.then((data) => {console.log(data);})/123Promise.reject() 返回一个带有明确data且状态为 rejected的 Promise对象.
1 2let p = Promise.reject(123); p.then(()=>{},(data) => {console.log(data);})/123Promise.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) })
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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);})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

> 可知, 只要有一个 触发 reject 就会触发,并且只返回第一个reject的p的数据
> 执行时间取决于第一个触发 rejuect的promise对象.
> Promise.all() 的用处
> 比如, 我要请求三次数据, 我可以每次获取数据都渲染一次页面,(3次)
> 也可以三个数据都获得之后 再渲染一次页面(1次)
> 但只要有一个数据是无法获取, 就要渲染一个 error提示页面
> 这个时候用 Promise.all() 就很适合.
> 类似的, 对于管理多个并行异步请求, 似乎是比较适合的.
> 并且这几个请求缺一不可.
> 这里的并行异步请求 指的是 各个请求之间没有顺序上的依赖.
> 如果存在顺序上的依赖, 明显用then 比较合适.
--------------------
> Promise.race([p1,p2,p3])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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);})
1
2
3
4
5
6
7
> 可知
> Promise.race() 求的是三个请求中最快的那个,
> 不关心 请求的结果如何.(这样就没什么太大意思啊)
> 通常用于 网络测试?
-----------------------------------------
Promise.prototype.catch()
1
2
3
4
5
6
7
8
9
10
11
12
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 模拟参数校验, 以及基本架构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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 模拟状态不可逆
1
2
3
4
5
6
7
8
9
10
11
12
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
1
2
3
4
5
6
7
8
9
10
static resolve (data) {
return new myPromise((suc) => {
suc(data);
})
}
static reject (data) {
return new myPromise((suc,err) => {
err(data);
})
}
step4 模拟 then
1
2
3
4
5
6
7
8
then (resolveFn, rejectFn) {
if (this.state == "resolved") {
resolveFn(this.data);
}
if (this.state == "rejected") {
rejectFn(this.data);
}
}
step5 判断回调的返回值是否为 promise 对象
1
2
3
4
5
6
7
8
9
10
11
12
13
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 对象, 这里是稍微难的, 感觉很巧妙.不容易想到.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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.reject(rus);
}
}
}
到此时,先贴一下整体代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
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.reject(rus);
}
}
}
static resolve (data) {
return new myPromise((suc) => {
suc(data);
})
}
static reject (data) {
return new myPromise((suc,err) => {
err(data);
})
}
}
不过无法解决 异步的问题.
作业1, 用 原生写一遍以上代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
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))
这样处理,能够完成第一步的异步, 但一旦链式调用, 则会报错
1
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的, 只不过方法稍微暴力了一点. 不对是非常的暴力, 感觉非常不可取, 但我们先写一下,,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
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))
貌似整个能够模拟今天所学的内容. 但异常暴力,感觉问题很大很大.. 不过思考两个小时,能够坚持思考,并敲一敲,还是挺欣慰的.
明天再看视频.