为什么使用Promise
如果新接触 Promise 的话,在网上能找到很多介绍 Promise 及其使用的文章(比如:ECMAScript 6 入门 / Promise 对象),这里就不赘述了,简而言之就是用来处理异步调用的一大利器。
微信小程序的API都可以传入函数 success,fail 和 complete 来实现异步回调。
样例一 :
-
// 显示”载入中”,在一秒后消失
-
wx.showLoading({
-
title:
"载入中",
-
success:
function () {
-
setTimeout(
function () {
-
wx.hideLoading()
-
},
1000)
-
},
-
fail:
function(){},
-
complete:
function(){}
-
});
原生的 success,fail 和 complete 已能够满足基本的异步回调了,但是如果遇到多个连续的阻塞任务,会造成多层嵌套(如样例二所示),就很奔溃。
样例二 :
-
// 显示“保存中”,一秒后隐藏,半秒后显示“载入中”,一秒后隐藏
-
wx.showLoading({
-
title:
"保存中",
-
success:
function () {
-
setTimeout(
function () {
-
wx.hideLoading({
-
success:
function () {
-
setTimeout(
function () {
-
wx.showLoading({
-
title:
"载入中",
-
success:
function () {
-
setTimeout(
function () {
-
wx.hideLoading()
-
},
1000)
-
}
-
})
-
},
500)
-
}
-
})
-
},
1000)
-
}
-
})
上面的例子有七个阻塞任务:显示“保存中”,停顿一秒,隐藏,停顿半秒,显示“载入中”,停顿一秒,隐藏。从直觉上来思考,这些任务应该是以队列的形式存在,一个完成了再开始下一个,而非层层嵌套,这也是使用Promise的一大原因,可以链式调用。
上面的例子如果用Promise封装之后的API来写,看起来就非常直观(样例三)
样例三 :
-
wsAPI
.taskSequence()
-
.then(
() => wsAPI.showLoading({
title:
"保存中"}))
-
.then(
() => wsAPI.sleep(
1000))
-
.then(
() => wsAPI.hideLoading())
-
.then(
() => wsAPI.sleep(
500))
-
.then(
() => wsAPI.showLoading({
title:
"载入中"}))
-
.then(
() => wsAPI.sleep(
1000))
-
.then(
() => wsAPI.hideLoading())
-
.then(
() =>
console.log(
"done"))
注: (A)=>{B} 是 ES6 的箭头函数,相当于 function(A){B},箭头函数不用显式 return。
比如 () => 5 就会 return 5
-
console.log((() => 5)()) // 5
-
封装实现
wsAPI的源代码实现如下:
-
let nullFn = () => {
-
};
-
function IllegalAPIException(name) {
-
this.message = "No Such API [" + name + "]";
-
this.name = 'IllegalAPIException';
-
}
-
let services = {
-
sleep: (time) => {
-
return new Promise(function (resolve, reject) {
-
setTimeout(resolve, time);
-
})
-
},
-
stop: () => {
-
return new Promise(function (resolve, reject) {
-
})
-
},
-
taskSequence: () => {
-
return new Promise(function (resolve, reject) {
-
resolve()
-
})
-
}
-
};
-
export let wsAPI = new Proxy(services, {
-
get: function (target, property) {
-
if (property in target) {
-
return target[property];
-
} else if (property in wx) {
-
return (obj) => {
-
return new Promise(function (resolve, reject) {
-
obj = obj || {};
-
obj.success = (...args) => {
-
resolve(...args)
-
};
-
obj.fail = (...args) => {
-
reject(...args);
-
};
-
obj.complete = nullFn;
-
wx[property](obj);
-
});
-
}
-
} else {
-
throw new IllegalAPIException(property);
-
}
-
-
}
-
});
-
wsAPI 用 Proxy(ECMAScript 6 入门 / Proxy)重新封装了 wx 的所有API。并新增了 sleep ,stop 和 taskSequence。sleep 用于阻塞一段时间;taskSequence 是一个空的 Promise,让代码看起来更整齐美观,可读性更好(样例四);stop 用于停止任务序列进行下去(样例五)
样例四 :
-
// taskSequence
-
wsAPI
.taskSequence()
-
.then(
() => wsAPI.showLoading({
title:
"保存中"}))
-
.then(
() => wsAPI.sleep(
1000))
-
.then(
() => wsAPI.hideLoading())
-
.then(
() => wsAPI.sleep(
500))
-
.then(
() => wsAPI.showLoading({
title:
"载入中"}))
-
.then(
() => wsAPI.sleep(
1000))
-
.then(
() => wsAPI.hideLoading())
-
.then(
() =>
console.log(
"done"))
-
-
// 没有 taskSequence,第一个promise就和下面的不对齐
-
wsAPI
.showLoading({
title:
"保存中"})
-
.then(
() => wsAPI.sleep(
1000))
-
.then(
() => wsAPI.hideLoading())
-
.then(
() => wsAPI.sleep(
500))
-
.then(
() => wsAPI.showLoading({
title:
"载入中"}))
-
.then(
() => wsAPI.sleep(
1000))
-
.then(
() => wsAPI.hideLoading())
-
.then(
() =>
console.log(
"done"))
样例五 :
-
wsAPI
.taskSequence()
-
.then(
() => wsAPI.showModal({
title:
"保存",
content:
"确定保存?"}))
-
.then(
res => {
-
if (!res.confirm) {
-
return wsAPI
.stop();
-
}
-
})
-
.then(
() =>
console.log(
"to save"))
-
.then(
() => wsAPI.showLoading({
title:
"保存中"}))
-
.then(
() => wsAPI.sleep(
1000))
-
.then(
() => wsAPI.hideLoading())
-
.then(
() =>
console.log(
"done"))