在JavaScript中,提供了一些异步特性,因为同步操作会对程序的执行进行阻塞处理。比如在浏览器页面程序中,如果一段同步的代码需要执行很长时间(比如一个很大的循环操作),则页面会产生卡死的现象。
异步为程序提供了性能和体验上的益处,比如可以将代码放到setTimeout()中执行;或者在网页中,我们使用Ajax的方式向服务器端做异步数据请求。这些异步的代码不会阻塞当前的界面主进程,界面还是可以灵活的进行操作,等到异步代码执行完成,再做相应的处理。
举一个例子:在小程序中,我们获取到后台的数据使用时间往往是不定的,这个时候:即使你在前面写的代码,也可能会在后面执行。
function getAccountInfo(callback, errorCallback) { wx.request({ url: '/accounts/12345', success: function (res) { console("1") }, fail: function (res) { //... errorCallback(data); } }); console.log("2") }
也就是存在2输出在1前面的可能。这种策略提升页面加载速度。能很好的提高用户体验感。
但是请看下面的情景:
小程序是提倡不获取用户信息就能使用的,但是在特定的情况下我们必须要拿到用户的一些数据,比如名称,图像链接等等。在这种情况下,我们首先获取用户的code,根据code去后台获取用户的openid或者unionid。拿到这些数据之后我们才能确定用户的身份,然后再去发新的请求(这些请求往往是需要用户身份凭证的,比如Token)。
上面的叙述中,我们发了三次请求,第一次获取code,第二次获取openID或unionid,第三次根据身份信息执行新的请求。我们都知道JS中的请求都是异步执行的,有可能微信服务端的code还没返回,用户已经去执行一些需要权限的操作,这就会导致请求失败。那么小程序该如何解决这种问题呢?这里提供三种方案以供参考:
1.服务端一次请求全部处理。(使用范围太小)
2.客户端在请求成功的回调中,再次发送请求。(可用,但是代码会很冗长且不容易维护)
3.使用Promise
方案一中:比如用户留言功能,我们拿到code和留言内容后全部发给服务端,服务端开启一个新的线程去处理这些业务逻辑,主线程直接返回用户留言成功的提示(这里不考虑新线程执行失败的情况)。
方案二和方案三功能上是相同的,但是代码的展示上可能方案三更加好一些。看下面的代码(其中postReq和getReq是自己封装的请求方法):
var http = require('request.js') function userLogin(name, image, gender, content, artilceId){ //获取code var promise = new Promise(function(resolve,reject){ wx.login({ success: res => { resolve(res.code); } }) }) //获取用户身份凭证 var pm2 = promise.then(function(res){ return new Promise(function (resolve, reject){ http.postReq("user/getUserInfo", { "code": res, "type": 1, "name": name, "image": image, "gender": gender }, function (res) { // console.log(res) if (res.data == "") { wx.showModal({ title: '提示', content: '授权失败,请重试', }) return; } resolve(res); }) }) },function(res){ }) //发表评论 pm2.then(function(res){ // console.log(res.data+":"+content) http.postReq("user/comment", { "content": content, "openId": res.data, "artilceId": artilceId},function(res){ // console.log(res) if(res.data ){ wx.getStorage({ key: 'showInfo', success: function(res) { console.log(res.data ) if(res.data != false){ wx.showModal({ title: '提示', content: '为了维护每天学Java的学习氛围,我们已发邮件提醒管理员对评论进行审核,通过后即可展示您的评论。', }) } },fail:function(res){ wx.setStorage({ key: 'showInfo', data: false, }) wx.showModal({ title: '提示', content: '为了维护每天学Java的学习氛围,我们已发邮件提醒管理员对评论进行审核,通过后即可展示您的评论。', }) } }) } }) }, function(res){ }) } module.exports = { userLogin: userLogin }
这里使用的就是Promise?对于Promise如何使用我们稍后再说,初学阶段我们可能会这样保证同步:
app.postReq("/homework/getHomeWorkLike",{"name":name},function(res){ app.postReq("/homework/getHomeWorkLike",{"name":name},function(res){ app.postReq("/homework/getHomeWorkLike",{"name":name},function(res){ } }) } }) } })
在每次返回成功的回调中执行新的请求,但是当我们处理的业务逻辑过多时候,就会显得很难看。因为不直观,所以不方便我们排查一些错误。所以推荐大家使用Promise。
Promise :
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。
所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
Promise对象有以下两个特点。
(1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
注意,为了行文方便,本章后面的resolved统一只指fulfilled状态,不包含rejected状态。
有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。
Promise也有一些缺点。首先,无法取消Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。第三,当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
如果某些事件不断地反复发生,一般来说,使用 Stream 模式是比部署Promise更好的选择。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。