持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第33天,点击查看活动详情
前言:
例如:请问,打印的结果是什么?
function sum1(a, b) {
setTimeout(() => {
return a + b;
}, 2000);
}
var result = sum1(1, 2);
console.log(result, "result");
复制代码
分析:函数中有定时器(异步),且sum1中没有同步写return,所以默认return Undefined。
所以result的值为undefined。
如何解决这个问题? 使用回调函数的方式。
什么是回调函数呢,就是定义一个函数,把它作为结果嵌套进原有函数中。例如:
function sum(a, b, callback) {
setTimeout(() => {
var result = a + b
callback(result)
}, 2000);
}
// 定义一个函数callback,作为参数代入sum函数中
function callback(result) {
console.log(result); //2
}
sum(1, 2, callback)
复制代码
但如果callback中也有异步代码,那岂不是要在callback中也传入一个函数作为参数,如果参数中还有异步代码,那也要再写回调。
如此,是我们经常所说的回调地狱问题。
在很多高级的接口中都会有解决回调异步的代码,这也是前端的强大之处,可以做到像java中的线程池一样同时承载千万的登陆访问。
在react中,由于setState为异步函数,在之前的文章中解决异步总是会在合成事件中写回调来获取同步的结果。但存在问题,倘若无止境的写回调函数是不是会出现回调地狱的问题呢。当然。
js中解决异步的方式:
1.回调函数 =>第二个参数传递回调函数
2.promise
3.async await
下面来解决react中的异步问题:
场景:点击改变值,同时获取值。
回调函数:
在合成事件中调用setState时,同时写一个回调函数来同步获取值,这是我们的第一种方法,改变异步为同步。
class App extends React.Component {
state = { a: 0 }
handleClick = () => {
this.setState({a:1},()=>{
this.state.a
})
}
render() {
return (
<div>
<div>{this.state.a}</div>
<button onClick={this.handleClick}>按钮</button> </div>
)
}
}
复制代码
promise
自己封装一个promise函数,当点击按钮触发时,同步获取改变后的值。
setStatePromise = (value) => {
return new Promise((resolve, reject) => {
if (typeof value === 'object' || typeof value === 'function') {
this.setState(value, resolve())
} else {
reject('setStatePromise 参数不对')
}
})
}
复制代码
class App extends React.Component {
state = { a: 0 }
handleClick = () => {
this.setStatePromise({a:1})
.then(_=>{
console.log(this.state.a); // 输出1
})
.catch(err=>{
console.log(err)
})
}
}
复制代码
以上解决了异步问题。
在返回promise函数时,有一个判断语句:是否为函数/对象。所以如果传入int类型的值,或字符串都会触发reject()函数返回error。
promise也可以有依赖上一次setState的结果功能。
例如:
this.setStatePromise({a:10})
this.setStatePromise((preState,prosp)=>{
return {a:preState.a +1} //返回11
})
.then(()=>{
console.log('promise获取值:',this.state.a);
})
复制代码
调用自己封装的promise方法,传入一个函数,分别设置两个参数,在preState中,就可以获取到依赖的上一次setStatePromise的值。
如果setStatePromise的参数传入10,或‘10’,都会触发reject()
坑:但是如果【1,2,3】,传入一个数组,那结果会是什么呢?
已知,数组是一个特殊的对象,所以,并不会触发reject返回err,而是resole()后返回undefined。
注意:不要忘记这一点。
async await
同样,点击按钮调用方法。其中setStatePromise与上文中的方法一样。此处不重复赘述。
handleclick=()=>{
await this.setStatePromise({a:1})
console.log(this.state.a);//setState 变成同步打印1
}
复制代码
依赖问题:
await this.setStatePromise((pState, props) => {
return { a: pState.a + 1 } //1
})
await this.setStatePromise((pState, props) => {
return { a: pState.a + 1 } //2
})
console.log(this.state.a);//setState 变成同步,有依赖
复制代码
try,catch:
async await 函数 错误捕获: try...catch
try {
await this.setStatePromise(10)
} catch (error) {
console.log(error); //不会爆红,是语法中失败的情况。
}
复制代码
setState原理---手写setState
setState是如何改变并更新state中的状态值呢?
- 首先一定会传入一个value值,为增加或者想要修改的对象。比如增加一个对象或者修改对象的值。
- 其次,要把原来的state值与后传入的新value合并在一起,放入空对象中。
用到的方法:归并对象,如果key相同的话,那就修改value,如果key不同的话,那就增添一个键值对。
数据合并 : Object.assign(target,...source) 将源数据 合并到目标数据 ,返回值就是合并后结果
代入手写原理中,则为:
let obj = Object.assign({}, this.state, newState)
//分别为 {}空对象,原数据,新数据
复制代码
然后再将obj替换给原先的state对象。
this.state = obj
复制代码
更改完数据之后,就可以触发render更新页面了。
此处使用强制更新:
// 触发render
this.forceUpdate()
复制代码
代码实现:
//原数据
state1 = { name: '', age: 18, gender: 1 }
//点击触发方法
handleClick1 = async () => {
this.setState1({ name: 'lisi', age: 21 })
}
//被调用的手写setState方法
setState1 = (newState) => {
//key相同,就替换掉
//修改state1 数据: {}+ {name:'',age:18,gender:1} + {name:'lisi'}=>{name:'lisi',age:18,gender:1}
// 数据合并 : Object.assign(target,...source) 源数据 合并到目标数据 ,返回值就是合并后结果
let obj = Object.assign({}, this.state1, newState)
//追加完之后,替换值
this.state1 = obj
// 触发render
this.forceUpdate()
}
<button onClick={this.handleClick1}>click1</button>
复制代码
setState 完结撒花。