try catch 与 promise

(763) 219-0956单纯的使用promise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const getData = (flag) => new Promise((res, rej) => {
setTimeout(() => {
flag ? res('resolved') : rej('rejected')
}, 3000)
})

let pro = getData(false)
pro.then(res => {
console.log(res)
}, rej => {
console.log(rej) /预期进入此函数
})

/ 推荐方式
pro.then(res => {
console.log(res)
})
.catch(err => {
console.log(err) /预期进入此函数
})
/ 输出如下
/ rejected rejected

倘若 promise 中运行出错了呢?我们就需要在getData 方法中添加try catch

1
2
3
4
5
6
7
8
9
const getData = (flag) => new Promise((res, rej) => {
try {
setTimeout(() => {
flag ? res('resolved') : rej('rejected')
}, 3000)
} catch (error) {
rej(error)
}
})

倘若then 方法中出错了呐?对比两种方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let pro = getData(true)
pro.then(res => {
throw '123'
console.log(res)
}, rej => {
console.log('方式1', rej)
})

pro.then(res => {
throw '123'
console.log(res)
})
.catch(err => {
console.log('方式2', err)
})
/ 输出如下
/ 方式2 123
/(node:439872) UnhandledPromiseRejectionWarning: 123
/(node:439872) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a
/catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
/(node:439872) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

由此可见:可见方式2代码更加健壮

5408164136配合async await

1
2
3
4
5
6
7
8
9
10
11
12
async function test(flag) {
let ans = await getData(flag)
console.log('---', ans) / 这句代码在flag = false 时,不会执行
}
test(true)
/ --- resolved
test(false)
/ 代码报错
/(node:446292) UnhandledPromiseRejectionWarning: error
/(node:446292) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a
/catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 3)
/(node:446292) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

发现rejected的状态ans并不能正常赋值,也就是说console.log(‘—‘, ans)并没有执行

async await下该如何捕捉异常

1
2
3
4
5
6
7
8
9
10
11
12
13
async function test(flag) {
try {
let ans = await getData(flag)
console.log('---', ans)
} catch (error) {
console.log(error)
}
}
test(false)
/ 输出如下
/ --- resolved
test(true)
// --- rejected

倘若getData中throw错误呐

1
2
3
4
5
6
7
const getData = (flag) => new Promise((res, rej) => {
throw 'error'
})

test()
/ 输出如下
/ --- error

综上:使用await 时只有promise的状态变为resolved时,他才能正常赋值

(822) 285-9201强化记忆

判断以下情形下的打印输出

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
/ 情况一
let a = new Promise((resolve, reject) => {
setTimeout(() => resolve(33), 2000)
}).then(res => {
getData(false).catch(err => {
console.log('-------')
})
}).catch(err => {
console.log('+++++++')
})
/ -------

/情况二
let a = new Promise((resolve, reject) => {
setTimeout(() => resolve(33), 2000)
}).then(res => {
getData(false).catch(err => {
console.log('-------')
return 33
})
}).catch(err => {
console.log('+++++++')
})
/ -------

/情况三
let a = new Promise((resolve, reject) => {
setTimeout(() => resolve(33), 2000)
}).then(res => {
return getData(false).catch(err => {
console.log('-------')
return Promise.reject(33)
})
}).catch(err => {
console.log('+++++++')
})
/ then 方法中有返回值
/ -------
/ +++++++

/情况四
let a = new Promise((resolve, reject) => {
setTimeout(() => resolve(33), 2000)
}).then(res => {
d
return getData(false).catch(err => {
console.log('-------')
return Promise.reject(33)
})
}).catch(err => {
console.log('+++++++')
})
/ d 未定义,相当于return Promise.reject(Error())
/ +++++++

831-392-9554建议

  1. 手动封装请求数据的函数时,要在函数中添加try catch
  2. promise建议使用 .then .catch 的格式
  3. async await 中要使用try catch,不然前面的代码 变为rejected时,后续的代码不会执行,当然除非你想要的就是这种效果
  4. 一个async函数中,如果两个请求数据有前置关系,可以连续await
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    / a 的结果 是请求b 的参数
    async test() {
    let a = await getData()
    let b = await getData(a)
    }

    / 当请求之间没有前置关系时, 使用promise.all() 可以使请求同时发出
    / 当然网速 快的情况下,二者 差别不大
    async test() {
    let ans = await Promise.all([getData(), getData()])
    }

forEach中的async和await

(516) 496-4162在for循环中使用async

1
2
3
4
5
6
7
8
9
10
11
const getData = () => new Promise(res => {
setTimeout(() => res(), 2000)
})

async function myFor() {
for(let i = 0; i < 4; i++) {
await getData()
console.log(i, 'current time: ', Date.now())
}
}
myFor()

402-434-4794运行截图

可以看出在for循环中是我们预期的效果,每隔2秒打印一个console

mudee在forEach中使用async

1
2
3
4
5
6
7
const getData = () => new Promise(res => {
setTimeout(() => res(), 2000)
})
let a = [1,1,1,1].forEach(async (item, index) => {
await getData()
console.log(index, 'current time: ', Date.now())
})

运行截图

可以看出在forEachEach中,打印的console是在同一个时间,也就是说我们模拟的请求数据几乎并行去执行的

844-744-0338究其原因

先简单思考一个问题,当我们给一个函数传递的参数是一个function时,那这个参数是如何使用的呐?

猜想一下:在forEach的函数估计也是调用一下

1
2
3
4
5
6
7
forEach(a) {
/ balabalabala...
/ 调用一下
a()
/ balabalaalala...
/ 达到循环效果
}

那么既然是这样那他跟for循环的区别就出来了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/ for循环的最终效果
async function MyFor() {
await getData()
await getData()
await getData()
await getData()
}
/ 而forEach的最终效果
const a = async (item, index) => {
await getData()
}
function MyForEach() {
a(1, 0)
a(1, 1)
a(1, 2)
a(1, 3)
}

区别显而易见:对于MyForEach,就是同步的执行a函数,他们之间并没有await关系,所以我们最终在console中看到他们几乎同时输出。

(410) 315-3726

Promise 个人感觉就是通过巧妙的方法,让函数按照某种顺序去执行,是一个精密的函数的调度过程。
其关键点两处:一个在于new 一个Promise时传入的 resolve函数和 reject函数是异步执行的;另一个就是 then 函数的实现。
具体参考Promise库的实现

2818843585代码实现

代码地址

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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
const PENDING = 0
const FULFILLED = 1
const REJECTED = 2
/ 首先定义三种状态,每个promise的状态只允许从pending -> fulfilled 或者 pending -> rejected
const StaticResolve = function (value) {
/ 只有当state是Pending时才允许改变
if (this._state !== PENDING) return
/ 此时状态从pending -> fulfilled,当前的value 实际上通过 new Promise((resolve, reject) => {resolve(value)}) 这种方式传递过来的
this._value = value
this._state = FULFILLED
/ 这里就相当于调用then(res => console.log(res), rej => console.log(rej))中的 ResolveCallBack 实际是 res => console.log(res) 的一个封装,同理
/ RejectCallBack 是 rej => console.log(rej) 的一个封装
/ 这里使用setTimeout 模拟异步,实际上源码中不是这么做的,貌似是用的asap
setTimeout(() => {
while (this._deferreds.length > 0) {
let defer = this._deferreds.shift()
defer.ResolveCallBack(value)
}
}, 0)

}
/ 跟 StaticResolve 大同小异
const StaticReject = function (value) {
if (this._state !== PENDING) return
this._value = value
this._state = REJECTED
setTimeout(() => {
while (this._deferreds.length > 0) {
let defer = this._deferreds.shift()
defer.RejectCallBack(value)
}
}, 0)
}
class MyPromise {
constructor(fnc) {
this._state = PENDING
this._value = null
this._deferreds = []
/ 这里在new 的过程立即调用fun函数,Promise 也是这么做的
fnc(StaticResolve.bind(this), StaticReject.bind(this))
}

then(resCallBack, rejCallBack) {
let _promise = this
/ then函数执行的时候干了两件事
/ 1、将传过来的resCallBack函数和rejCallBack函数封装成一个defer对象,然后push到上一个promise的_deferreds属性中,如果上一个promise状态不为pending,则直接执行
/ 2、返回一个新的Prosime ---这里理解是一定要记得结合上面的fnc(StaticResolve.bind(this), StaticReject.bind(this))代码
/ 这些代码都是同步的除了我们setTimeout,也就是说当你在new MyProsime().then()时他们是同步执行的
return new MyPromise(function (resolve, reject) {
let defer = {
ResolveCallBack(value) {
try {
if (typeof resCallBack !== 'function') {
throw TypeError('参数类型错误')
}
let res = resCallBack(value)
if (res instanceof MyPromise) {
res.then(function (val) {
resolve(val)
}, function (val) {
reject(val)
})
} else {
resolve(res)
}
} catch (error) {
reject(error)
}

},
RejectCallBack(value) {
try {
if (rejCallBack !== undefined) {
if (typeof rejCallBack !== 'function') {
throw TypeError('参数类型错误')
}
let rej = rejCallBack(value)
reject(rej)
}
reject(value)
} catch (error) {
reject(error)
}
}
}
if (_promise._state === PENDING) {
_promise._deferreds.push(defer)
} else if (_promise._state === FULFILLED) {
defer.ResolveCallBack(_promise._value)
} else if (_promise._state === REJECTED) {
defer.RejectCallBack(_promise._value)
}
})
}
/ 一下catch,resolve,reject all方法实际上都是在原有已实现代码导航的简单封装
catch(error) {
return this.then(() => { }, error)
}

static resolve(value) {
return new MyPromise(res => res(value))
}

static reject(value) {
return new MyPromise((res, rej) => rej(value))
}

static all(promises) {
if(!Array.isArray(promises)) {
throw TypeError('参数类型错误')
}
return new MyPromise((resolve, reject) => {
let values = []
let len = promises.length
promises.forEach((item, index) => {
item.then(res => {
len--
values[index] = res
if(len === 0) {
resolve(values)
}
}, rej => {
reject(rej)
})
})
})
}
}

测试用例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
let p1 = new MyPromise((res, rej) => {
setTimeout(() => {
res('delay 5s')
}, 5000)
})
p1.then(res => {
console.log(res)
return res + ' success'
})
.then(res => {
console.log(res)
})
let p2 = new MyPromise((res, rej) => {
setTimeout(() => {
rej('delay 3s')
}, 3000)
})

MyPromise.all([p1, p2])
.then(res => {
console.log(res)
}, rej => {
console.log(rej)
})

7245760915个人疑惑

js原型

编写一个People类

1
2
3
4
5
6
7
8
9
10
11
12
var People = function(a, b) {
this.name = a;
this.sex = b;
}
People.prototype = {
x: '123',
y: '456'
}
var pa = new People('小明', '男');
var pb = new People('小红', '女');
pa.x = 'fix';
console.log(pa, pb, pa.__proto__ === pb.__proto__)

8195064416运行如下


由此可见:

  • 两个对象拥有相同的proto
  • 当访问对象某个属性时,首先查找实例,实例不存在时,沿着原型查找;
  • 当试图修改某个属性时,倘若对象本身拥有,则直接修改;若本身没有,直接在实例新建属性

js 函数节流与函数去抖


814-285-5689函数节流 throttle


个人理解:只允许函数在一个周期时间T内只执行一次,即使在T时间内调用了多次

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
*
* @param {Object} context
* @param {Function} fun
* @param {Number} delay ms
*/
function throttle(context, fun, delay) {
var pre = Date.now();
return function() {
var now = Date.now();
if(now - pre > delay) {
fun.apply(context);
pre = now;
}
}
}

函数防抖 debounce


个人理解:限制函数在调用后延迟 T 秒执行,倘若在 T 时间内再次调用,则延迟计时重新开始

740-877-7543代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
*
* @param {Object} context
* @param {Function} fun
* @param {Number} delay ms
*/
function debounce(context, fun, delay) {
var pre = null;
return function() {
if(pre) {
clearTimeout(pre);
}
pre = setTimeout(function() {
fun.apply(context)
}, delay);
}
}

业务场景


  1. 调整浏览器窗口大小,监听resize
  2. keypress on autocomplete form with Ajax request(填写完成后自动发送请求)
  3. Infinite scrolling (无限滚动)

相关资料