若是新接觸 Promise 的話,在網上能找到不少介紹 Promise 及其使用的文章(好比:ECMAScript 6 入門 / Promise 對象),這裏就不贅述了,簡而言之就是用來處理異步調用的一大利器。小程序
微信小程序的API均可以傳入函數 success,fail 和 complete 來實現異步回調。微信小程序
樣例一promise
// 顯示」載入中」,在一秒後消失 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的一大緣由,能夠鏈式調用。ide
上面的例子若是用Promise封裝以後的API來寫,看起來就很是直觀(樣例三)函數
樣例三this
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。spa
好比 () => 5 就會 return 5code
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"))