道招

使用setTimeout以及async await的误区

如果您发现本文排版有问题,可以先点击下面的链接切换至老版进行查看!!!

使用setTimeout以及async await的误区

我们平时在遇到很多比较怪异的事情的时候,我们会使用setTimeout这样的奇淫异技,比如避免执行的太快了,某些数据还没有来等,我之前基本不用这个,但是在最近的一个项目中用到了。 项目大致是这样的,用的element-ui的el-upload来实现图片上传,就是一个弹框,里面有很多tab页,每个tab也都有el-upload,只是能每个上传的参数和地址什么的是不同的。

<div>操作类型</div>
<el-radio
    v-model="currentSelect"
    v-for="(item, index) in configs"
    :label="index"
    :key="'operateRadio' + index"
    >{{ item.label }}
</el-radio>
<el-upload :data="data" :action="url" :auto-upload="false" ref="elUpload">
    <el-button type="primary" size="small" @click="submit">上传文件</el-button>
</el-upload>
props:[''configs"]
data(){
    data: "",
    url: "",
    currentSelect: 0,
},
computed: {
    config() {
       return this.configs[this.currentSelect] || {};
    },
},
methods: {
    submit(){
        this.data = this.config.data;
        this.url = this.config.url;
        console.log(this.data, this.url);
        this.$refs.elUpload.submit();
    }
}

当时的问题是,每次在用户点击切换了操作模式后点击上传没有问题,如果不点的话用(默认的第一种)一般也没有问题,只是偶尔会出现传递给el-upload的data参数再上传时没有传递给action的接口地址的情况, 但是在用在调用this.$refs.elUpload.submit();上传之前打印console.log(this.data, this.url);确实值已经打印了,他们的指都是字符串,不存在console打印“不准”的情况。最后解决这个办法就是用了setTimeout:

this.data = this.config.data;
this.url = this.config.url;
setTimeout(() => { 
    this.$refs.elUpload.submit();
}, 200);

感觉可能是el-upload监听prop的变化不及时,或者watch时没有用类似immediate: true,之类的吧。

现在大家一般都知道一些事件队列的知识,比如setTimeout是属于宏队列,Promise是微队列。周五就看到有同事写了类似这样的代码:

const arr = []
list.forEach(async item => {
  const res = await axios.post(item.url);
  arr.push(res.data.time);
})
setTimeout(() => {
    console.log('arr ', arr);
}, 1000);

然后在arr里面有时取不到值跑过来问我,我问他为什么这么写,然后他就给我说了宏队列、微队列那一套,还跟我说什么用async await把异步变成同步了。。。

然后我让他用Promise.all来写

const arr = await Promise.all(list.map(item => axios.post(item.url))).map(item => item.data.time);
console.log('arr ', arr);

这样肯定没问题的。

我顺便写了个demo让他彻底明白,自己的那种setTimeout骚操作是多么的不靠谱 先用express写个后端接口

// server.js
const app = require('express')();
app.post('*', (req, res, next) => {
  setTimeout(() => {
    res.send({
      post: Date.now()
    });
  }, 4000)
});
app.listen(3000);

客户端代码如下:

// client.js
const result = [];
list.forEach(async item => {
  const res = await axios.post('http://localhost:3000/post');
  result.push(res.data.time);
})
setTimeout(() => {
  console.log('执行settimeout', result);
}, 600);
console.log('同步', result);

执行结果如下

同步 []
执行settimeout []
接口数据 1551629543195
接口数据 1551629543195

细心的读者可能看到上面两个setTimeout的时间了,后端接口是4000毫秒,前端这边是600毫秒,这种情况下,前端的setTimeout是百分之百拿不到数据的,如果前端把600改成6000就可以拿到了。

同步 []
接口数据 1551629811109
接口数据 1551629811111
执行settimeout [ 1551629811109, 1551629811111 ]

但是实际情况能这样用吗?肯定不行,你怎么知道接口大概多久能返回接口,这不是胡闹吗?

写累了,开始总结吧。 首先是在同一个事件循环里面微队列永远在宏队列前面执行,async await只是把异步的行为用同步的写法写出来,而非是把异步变成同步,这样更符合我们的同步思维罢了。

更新时间:
上一篇:道招网升级故障记录下一篇:准备做点开源前端练手小项目

相关文章

关注道招网公众帐号
友情链接
消息推送
道招网关注互联网,分享IT资讯,前沿科技、编程技术,是否允许文章更新后推送通知消息。
允许
不用了