同步异步(wx.request+ajax)
文章目录
前言
以前,一直觉得一个程序,总应该根据代码逻辑的顺序运行。即使中间经历再多的逻辑,也应该完成后回应才是正常的。但是如果一个方法的响应过久,那么用户体验就非常的不友好。如果跳过了某个逻辑,又不符合正常业务需求。
所以整理以下 同步与异步 的知识。
系列
同步异步(wx.request+ajax)
消息队列的学习与理解
消息队列之RabbitMQ的学习
RabbitMQ的应用之spring-boot-starter-amqp
时间线
- 2020.09.07-完成初稿
- 2020.09.08-补充几个名词概念解析
参考链接
JavaScript异步教程(非常好的一个教程,就是代码有点乱)
同步与异步
定义
关于同步异步的定义,虽然讲的是那么个意思,但是很容易弄混两者的定义。
同步是指程序根据代码的顺序从上到下地一次性执行
而相反,异步是指程序并非一次性执行的,有一部分现在执行,有一部未来执行。也可以使用“跳步”来解释。
如果对CAP原理比较熟悉,那么其中的Consistency一致性的3种分类,可以用来表示同步与异步。
同步指的是强一致性,而异步指的是最终一致性。
同步变异步
第一次接触到同步变异步的概念是源于粉丝订阅博主需要发送邮件的业务逻辑。例如场景如下:
当然,这里不讲如何实现的,因为每一种语言都有不同的生态,例如可以使用Redis的队列,或者Java使用消息队列等等。
发送邮件的时间是非常长的,但是又与主逻辑不冲突,所以可以把发送邮件的逻辑放在另一个队列完成,不仅仅可以不影响注册逻辑的正常返回,反而能提升订阅的响应时间。
思考:为什么不每个逻辑都使用异步?
我们常常通过同步变异步的方式提高响应,解耦合等,但是有些时候,有些逻辑是不可以放在另一条队列实现。
如果下一个逻辑处理的结果需要哦前一个逻辑处理的返回值,那么就不能使用异步。例如使用手机号注册的逻辑。就应该如下:
虽然发送短信的时间可能会长达几秒,但是如果注册时不对手机号进行验证,那么就有可能使用他人手机号进行验证。这也是许多注册逻辑时,有个发送验证码倒计时组件,最短时间都设置为1分钟,而正常一个逻辑,只要几秒或者十几秒。设置为1分钟,这也是为了提升用户的感觉——我都准备等1分钟了,没想到十几秒就完成了。
异步变同步:微信小程序的wx:request同步化
异步变同步,并不是开倒车,而是贴近现实。
在一次微信小程序开发中,上传图片时出现了响应结果丢失的情况,各种调试之后,才发现原来wx.request的原因。
场景复原
由于上传图片逻辑太多,所以模拟一个简单请求。
后端请求
from flask import Flask,request,jsonify
app = Flask(__name__)
import json
@app.route('/test')
def test():
arg_number = request.args.get("number")
return jsonify({
'code':1,
'msg':"success",
'data':arg_number
})
if __name__ == "__main__":
app.run(debug=True, host='127.0.0.1', port=8080)
小程序端
/**
* 表单函数
*/
submit: function(number){
wx.request({
url: 'http://localhost:8080/test',
data: {
number:number
},
success:function(res){
return res.data.data;
},
fail:function(res){
console.log("发送错误")
}
})
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
var that = this;
console.log("start");
var resList = [];
for(var i = 0; i < 5; i++){
var res = this.submit(i);
resList.push(res);
}
console.log(resList);
console.log("end");
}
预期结果
start//console.log("start");
[0,1,2,3,4]//resList
end//console.log("end");
实际结果
验证:函数submit是否有执行?
通过查看实际结果,可以看到resList是空的。
那函数submit是否有执行?
可以在submit函数处添加一个输出语句,即可完成验证,可以看到,其实是有调用的。
吐槽
查找了开发文档的wx:request内容,都没有关于异步的提示。
https://developers.weixin.qq.com/miniprogram/dev/api/network/request/wx.request.html
虽然知道wx.request和JS的ajax的用法差不多,ajax也是异步的,但是像Ajax一样注明以下更好
解决方案
如果想要将微信小程序的某个异步改变为同步,即wx.request同步化
可以使用Promise实现
改造小程序端函数
主要内容:Promise,async、await3个关键字
promiseSubmit:function(number){
return new Promise((resolve, reject)=>{
wx.request({
url: 'http://localhost:8080/test',
data: {
number:number
},
success:function(res){
console.log("开始返回值");
resolve(res.data.data);
},
fail:function(res){
console.log("发送错误")
reject(null);
}
})
});
},
onLoad: async function (options) {
var that = this;
console.log("start");
var resList = [];
for(var i = 0; i < 5; i++){
var res = await this.promiseSubmit(i);
resList.push(res);
}
console.log(resList);
console.log("end");
}
输出结果
符合预期结果
思考:工作机制
异步函数 async function 中可以使用 await 指令,await 指令后必须跟着一个 Promise,异步函数会在这个 Promise 运行中暂停,直到其运行结束再继续运行。
Ajax
Ajax其实跟wx.request是一样的,更确切地说,wx.request于小程序而言,相当于ajax于web端。
关于Ajax的内容会单独写一篇,这里就不扯远了
场景模拟
为了更加清晰地分析,所以使用flask框架,使用尽可能少的代码实现
后端请求
from flask import Flask, render_template, request, jsonify
import json
import time
app = Flask(__name__)
@app.route("/login",methods=["GET"])
def login():
print(request.method)
return render_template('register.html')
@app.route("/tologin",methods=["POST"])
def tologin():
data = request.get_data(as_text=True)
print(data)
timeStr = str(int(time.time()*1000))
return timeStr
if __name__ == '__main__':
app.run(debug=True,port=8080)
前端设计
由于更熟悉JQuery的Ajax,所以使用JQuery封装好的Ajax。
当然,也可以使用js的原生Ajax,但是使用有点复杂。
这里只截关键部分,关于html的内容,其实只有两个输入框和一个按钮组件而已。
<script src="http://libs.baidu.com/jquery/2.1.4/jquery.min.js"></script>
<script type="text/javascript">
function register() {
var userName = document.getElementById('username').value;
var passWd = document.getElementById('passwd').value;
// 方便分析 添加的时间戳
console.log("Ajax代码块前的时间戳:",new Date().getTime())
// Ajax
$.ajax({
type: 'POST',
url: '/tologin',
data: {
"username": userName,
"password": passWd
},
dataType: 'json',
success: function(res) {
// 方便分析 添加的时间戳
console.log("Ajax运行时的时间戳:",new Date().getTime())
alert("请求完成的时间戳:"+res)
},
error: function(err) {
}
});
// 方便分析 添加的时间戳
console.log("Ajax代码块后的时间戳:",new Date().getTime())
}
</script>
运行结果
总结
可以通过运行结果知道,Ajax的异步效果。
几个名词解析
关于这个模块,源于看到有篇博客将异步的架构说成是并行。
所以在此特别比较以下
名词 | 比较方式 | 定义 | 场景 |
---|---|---|---|
同步 | 是否开启新线程 | 不开启新线程 | |
异步 | 开启新线程 | ||
串行 | 任务的执行方式 | 指多个任务时,各个任务按顺序执行,完成之后才能进行下一个 | |
并行 | 多个任务同时执行 | ||
顺序 | 时间角度的理解 | ||
并发 |
题外话
-
并发并非真正的同时运行多个线程
如果系统只有一个CPU,则它根本不可以真正地运行一个以上的线程。
实际上,它只能把CPU运行时间划分成若干时间段,再将时间段分配给各个线程执行,
在一个时间段的线程代码运行时,其它线程处于挂起状态,这种方式称之为并发。
-
并行和并发
并行和并发又相似又有区别
并行是指两个或多个事件在同一时刻发生
而并发是指两个或两个时间在同一时间间隔内发生。
举个例子,在多个程序环境下,并发是指在一段时间内宏观上有多个程序在同时运行,
但在单处理系统上,每一个时刻仅有一个程序在执行,
微观上,这些程序是分开交替运行的,只是时间间隔特别地小
在多处理系统上,程序会被分发到多个处理机上,实现并行执行。
一些书籍的描述
-
七周七并发模型
并发是同一时间应对多个事情的能力;
并行是同一时间动手多多个事情的能力;
一般在计算机世界,这个主角主要是 处理器
总结
关于同步异步的内容其实还有很多深内容可以向下学习的。
例如,同步转异步的,就涉及到消息队列等等,
而异步转同步,涉及到的内容并不多,但是当没有这种意识又出现问题时,异步是非常难以捉摸到的。
所以,这也是上面内容中,多次使用时间戳,通过时间戳的大小关系,分析执行顺序。
另外,同步转异步固然有超多的好处,但是如果业务背景不符合的话,千万不能“拿来主义”,不思考就使用。