react异步回调里的setState执行流程

最近发现一个场景:请求的回调里setState时,setState后面的同步代码会在render完之后才会执行,而我的setState并前没有加await关键词变成同步的。

场景一:click调用setState

constructor(props) {
    super(props);
    this.state = {
        name:1
    }
}

click(){
  let _name = this.state.name
  console.log('click before')
  this.setState({
      name:++_name
  },()=>{
      console.log('click 回调')
  })
  console.log('click behind')
}

render() {
    console.log('render')
    return (
      <div onClick={this.click.bind(this)}>
         {this.state.name}
      </div>
    )
}
复制代码

执行结果

click before
click behind
render
click 回调
复制代码

解析

这种情况最常见,先执行同步代码,setState后触发render, render后会执行setState的回调,类似于vm.$nextTick()

场景二:click调用await setState

constructor(props) {
    super(props);
    this.state = {
        name:1
    }
}

async click(){
  let _name = this.state.name
  console.log('click before')
  await this.setState({
      name:++_name
  },()=>{
      console.log('click 回调')
  })
  console.log('click behind')
}

render() {
    console.log('render')
    return (
      <div onClick={this.click.bind(this)}>
         {this.state.name}
      </div>
    )
}
复制代码

执行结果

click before
render
click 回调
click behind
复制代码

解析

先执行同步部分输出click before,因为setState被await了,所以setState后面同步代码不会执行,等setState执行完后,先执行render,在执行setState的回调,最后执行await后面的同步代码。类似于await new Promise().then的形式:

console.log(0)
await new Promise((resolve)=>{
  resolve(1)
  this.setState({
      name:++_name
  },()=>{
      console.log(1.5)
  })
}).then(res=>{
  console.log(res)
})
console.log(2)

输出结果 
0
render
1.5
1
2
复制代码

场景三:异步回调里调用setState

constructor(props) {
    super(props);
    this.state = {
        name:1
    }
    // this.getData()
  }

  componentWillMount(){
     // this.getData()
  }
  componentDidMount(){
     // this.getData()
  }
getData(){
    fetch('/api',{
        method: 'post',
        credentials: 'same-origin',
        headers:{
            'Content-Type':'application/json',
        },
        body:JSON.stringify({
            
        })

    })
    .then(res=>res.json())
    .then(res=>{
        console.log('send before')
        this.setState({
            name:1000
        },()=>{
            console.log('send 回调')
        })
        console.log('send behind')
    })


    Promise.resolve().then(()=>{
        console.log('send before')
        this.setState({
            name:1000
        },()=>{
            console.log('send 回调')
        })
        console.log('send behind')
    }).then(()=>{
        console.log('send before')
        this.setState({
            name:1000
        },()=>{
            console.log('send 回调')
        })
        console.log('send behind')
    })


    setTimeout(()=>{
        console.log('send before')
        this.setState({
            name:1000
        },()=>{
            console.log('send 回调')
        })
        console.log('send behind')
    })
}
render() {
    console.log('render')
    return (
      <div onClick={this.getData.bind(this)}>
         {this.state.name}
      </div>
    )
}
复制代码

执行结果

以上不论是click触发getData方法,还是constructor、componentWillMount、componentDidMount里调用,只要setState在一个异步回调里执行,输出的结果都一样

render
send before
render
send 回调
send behind
复制代码

解析

可以发现setState放在异步回调里执行的时候,即使不加await setState,setStaet后面的同步代码也会等到setState执行完后render完和回调完成后才执行。表现跟场景二基本相同。

感觉就像是setState在异步回调里触发时,被强制加上了await变成了同步的,setState后面的同步语句必须要等到setState->render->setStaet callback执行完成后才轮到执行。

场景四:异步回调里调用多次setState

state = {
    a:0
}
click = () => {
	Promise.resolve().then(res => {
		this.setState({
			a: 1,
		})
		console.log(this.state.a)
		this.setState({
			a: this.state.a + 1,
		})
		console.log(this.state.a)
	})
	<!--或-->
	setTimeout(() => {
		this.setState({
			a: 1,
		})
		console.log(this.state.a)
		this.setState({
			a: this.state.a + 1,
		})
		console.log(this.state.a)
	})
}

render() {
	console.log('render')
	<div onClick={this.click}>
		点击
	</div>
	<div className="">{this.state.a}</div>
}
复制代码

执行结果

render //初始化render触发
// 点击按钮后
render 
1
render
2
// 页面显示2
复制代码
state = {
    a:0
}
click = async () => {
	await this.setState({
		a: 1,
	})
	console.log(this.state.a)
	this.setState({
		a: this.state.a + 1,
	})
	console.log(this.state.a)
	this.setState({
		a: this.state.a + 1,
	})
	console.log(this.state.a)
	this.setState({
		a: this.state.a + 1,
	})
	console.log(this.state.a)
}

render() {
	console.log('render')
	<div onClick={this.click}>
		点击
	</div>
	<div className="">{this.state.a}</div>
}
复制代码

执行结果

render //初始化render触发
// 点击按钮后
render 
1
render
2
render 
3
render
4
// 页面显示4
复制代码

解析

可见,在async函数里,只要第一次使用了await,以后的setState即使没有使用await关键词,也会同步执行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值