咱们知道,JavaScript是单进程执行的,同步操做会对程序的执行进行阻塞处理。好比在浏览器页面程序中,若是一段同步的代码须要执行很长时间(好比一个很大的循环操做),则页面会产生卡死的现象。php
因此,在JavaScript中,提供了一些异步特性,为程序提供了性能和体验上的益处,好比能够将代码放到setTimeout()中执行;或者在网页中,咱们使用Ajax的方式向服务器端作异步数据请求。这些异步的代码不会阻塞当前的界面主进程,界面仍是能够灵活的进行操做,等到异步代码执行完成,再作相应的处理。git
一段典型的异步代码相似这样:小程序
function asyncFunc(callback) { setTimeout(function () { //在这里写你的逻辑代码 //... //逻辑代码结束,执行一个回调函数 callback(); }, 5000); }
或者:微信小程序
function getAccountInfo(callback, errorCallback) { wx.request({ url: '/accounts/12345', success: function (res) { //... callback(data); }, fail: function (res) { //... errorCallback(data); } }); }
而后咱们这样调用:浏览器
asyncFunc(function () { console.log("asyncFunc() run complete"); }); getAccountInfo(function (data) { console.log("get account info successfully:", data); }, function () { console.error("get account info failed"); });
这是一种使用了回调函数来控制代码执行流程的方式。这样看起来没问题,也挺容易理解。服务器
可是,若是咱们一段代码中,异步操做太多,又要保证这些异步操做是有顺序的执行,那咱们的代码就看起来很是糟糕,就像这样:微信
asyncFunc1(function(){ //... asyncFunc2(function(){ //... asyncFunc3(function(){ //... asyncFunc4(function(){ //... asyncFunc5(function(){ //... }); }); }); }); });
这样的代码可读性和可维护性可想而知了。还有,回调函数真正的问题在于:app
它剥夺了咱们使用 return 和 throw 这些关键字的能力。
那有什么办法来改善这个问题呢?答案是确定的,Promise这种概念的产生,很好地解决了这一切。关于什么是Promise,一搜一大把介绍,我这里就不复制粘贴了,我主要是讲一下咱们怎么用它来解决咱们的问题。框架
咱们来看一下,上面的例子若是使用Promise,它会是什么样子?咱们先将这些函数变成Promise的方式:异步
function asyncFunc1(){ return new Promise(function (resolve, reject) { //... }) }
// asyncFunc2,3,4,5也实现成跟asyncFunc1同样的方式...
而后看一下他们是怎么样被调用的:
asyncFunc1() .then(asyncFunc2) .then(asyncFunc3) .then(asyncFunc4) .then(asyncFunc5);
这样,这些异步函数就会按照顺序一个一个依次执行了。
ES6中原生支持了Promise,不过在原生不支持Promise的环境中,咱们有不少第三方库来支持,好比Q.js和Bluebird。它们通常都除了提供标准Promise的API外,还提供了一些标准以外但很是有用的API,使得异步流程的控制更加优雅。
从微信小程序的API文档中咱们能够看到,框架提供的JavaScript API中不少函数其实都是异步的,如wx.setStorage(), wx.getStorage(), wx.getLocation()等等,它们也是提供的回调的处理方式,在参数中传入success, fail,complete回调函数,就能够对运行成功或失败进行分别处理。
如:
wx.getLocation({ type: 'wgs84', success: function(res) { var latitude = res.latitude var longitude = res.longitude var speed = res.speed var accuracy = res.accuracy }, fail: function() { console.error("get location failed") } })
咱们能不能让微信小程序的异步API支持Promise呢?答案是确定的,咱们固然能够一个一个的用Promise去包装这些API,可是这个仍是比较麻烦的。不过,因为小程序的API的参数格式都比较统一,只接受一个object参数,回调都是在这个参数中设置,因此,这为咱们的统一处理提供了便利,咱们能够写一个非侵入性的工具方法,来完成这样的工做:
假设咱们将这个工具方法写到一个名为的util.js的文件中:
var Promise = require('../libs/bluebird.min') //我用了bluebird.js function wxPromisify(fn) { return function (obj = {}) { return new Promise((resolve, reject) => { obj.success = function (res) { resolve(res) } obj.fail = function (res) { reject(res) } fn(obj) }) } } module.exports = { wxPromisify: wxPromisify }
以后,咱们来看一下如何使用这个方法,将原来回调方式的API变成Promise的方式:
var util = require('../utils/util') var getLocationPromisified = util.wxPromisify(wx.getLocation) getLocationPromisified({ type: 'wgs84' }).then(function (res) { var latitude = res.latitude var longitude = res.longitude var speed = res.speed var accuracy = res.accuracy }).catch(function () { console.error("get location failed") })
是否是很容易理解?
关于使用Promise处理异步流程,就先讲到这里,有什么疑问,能够留言给我。不对之处,欢迎指正。
相关阅读:
一斤代码深刻理解系列《一》:微信小程序事件机制
一斤代码深刻理解系列《二》:微信小程序样式机制
一斤代码深刻理解系列《三》:微信小程序和服务器通讯