Je pense que de nombreux développeurs ont rencontré le problème de l'enfer des rappels. Étant donné que les API des mini-programmes WeChat sont essentiellement des opérations asynchrones basées sur des fonctions de rappel, si vous n'utilisez pas d'autres frameworks ou d'API encapsulées, surtout si vous utilisez beaucoup de wx.request(), vous rencontrerez essentiellement le problème du rappel de l'enfer. rapidement. Entretien C'était très pénible.
Par exemple
Supposons que vous développiez actuellement une applet sociale. L'une des fonctions est qu'après s'être connectés, les utilisateurs de l'applet peuvent voir les personnes à proximité.
En supposant que l'idée d'implémentation suivante soit utilisée, nous obtenons l'emplacement actuel de l'utilisateur via wx.getLocation(), puis demandons les données du backend via wx.request(). Mais avant cela, vous devez vous connecter. Reportez-vous à la méthode de connexion recommandée par le document officiel précédent. Appelez d'abord wx.login() pour obtenir le code, puis utilisez wx.request() pour demander au serveur du développeur si le code est disponible. l'état de connexion personnalisé est renvoyé avec succès (généralement access_token ou autre forme de jeton), puis utilisez l'état de connexion personnalisé pour demander des données commerciales.
Pour faciliter la lecture, j'ai publié le processus de connexion dans le document officiel ⬇️
Une fois l'idée déterminée, commencez à essayer de coder (ce n'est pas recommandé de lire le code suivant) )
/* 以下为Page对象的方法 */ getNearby: function() { // 判断是否已认证,可采用wx.checkSession()方案 if (isAuth) { // TODO: 获取业务数据 return } // wx.login获取code wx.login({ success(res) { if (res.code) { // 获取自定义登录态 wx.request({ url, method, headers, data, success(res) { // 请求成功 if (res.statuCode === 200) { // 读取响应体中的自定义登录态 let token = res.data.token // 保存自定义登录态 wx.setStorageSync("assess_token", token) // 获取位置信息 wx.getLocation({ success(res) { let { latitude, longitude } = res // 请求业务数据 wx.request({ url, method, header, data: { latitude, longitude }, success(res) { // 请求成功 if (res.statuCode === 200) { let data = res.data // 数据渲染到V层 this.setData({ list: data }) } // 请求失败 else if (res.statuCode === 400) { // TODO } // 其他错误情况状态码处理 // TODO }, fail(err) { // 调用失败处理 } }) }, fail(err) { // 调用失败处理 } }) } // 请求失败 else if (res.statuCode == 400) { // TODO } // 其他错误情况的状态码处理 }, fail(err) { // 调用失败处理 } }) } else { // TODO // 登录失败 } }, fail(err) { // wx.login()调用失败处理 // TODO: ... } }) }
Callback L'enfer est apparu. Les codes des vagues de Qigong, sans parler des autres, vous rendront malade même si vous les regardez.
Un jour, le sage chef de produit s'est levé et a dit que nous pouvions ajouter du XXXXX. Vous devrez peut-être trouver un endroit pour imbriquer d'autres interfaces WeChat ou ajouter quelques if else
branches supplémentaires. pleurer.
Solution
Dans un sens, l'écosystème front-end orageux d'aujourd'hui repose sur l'émergence de Node et d'ES6+.
Il existe de nombreuses solutions pour l'asynchrone après ES6. La première consiste à utiliser generator/yield
, mais la fonction generator
est en réalité plus difficile à utiliser. L'autre consiste à utiliser Promise
, ce qui est relativement simple. ES7 peut également adopter async/await,
mais essentiellement async/awai
t est également basé sur Promise
. Présenté ci-dessous : Promise
.
Promesse
Création d'une promesse
Créer une promesse est très simple. La promesse elle-même est un constructeur. Créé via new. Le paramètre du constructeur est une fonction de rappel, et la fonction de rappel a deux paramètres : résoudre et rejeter (aucune maintenance manuelle requise). la résolution et le rejet sont utilisés pour changer d’état. Je parlerai du statut plus tard.
// Promise实例的创建 let p = new Promise((resolve, reject) => { // TODO })
La promesse a un inconvénient, elle sera exécutée immédiatement une fois créée. Il est donc généralement fourni avec une fonction.
let getPromise = () => { return new Promise((resolve, reject) => { // TODO }) }
Statut de la promesse
Les instances de promesse ont trois états, pending
, resolved
et rejected
Une fois l'instance Promise
créée, elle sera dans le pending
. État. resolve
et reject
dans la fonction de rappel sont utilisés pour changer le statut de l'instance Promise
. Lorsque resolve
est appelé, l'instance Promise
passe du statut pending
à resolved
, indiquant le succès. Lorsque reject
est appelé, l'instance Promise
passe du statut pending
à rejected
, indiquant un échec.
let getPromise = () => { return new Promise((resolve, reject) => { // TODO // 处理结果 if (result) { resolve(successObject) } else { reject(error) } }) }
Méthodes courantes
Les méthodes les plus couramment utilisées sont then
() et catch
(). L'enfer des rappels peut être résolu en passant l'utilitaire du problème then
(). .
Parmi eux, then
() peut recevoir deux paramètres, qui sont tous deux des fonctions de rappel. La première fonction de rappel est utilisée pour gérer le statut resolved
, et le paramètre est l'objet de réussite transmis par le. Promise
résolution d'appel d'instance. La deuxième fonction de rappel est utilisée pour gérer le statut rejected
, et le paramètre est l'objet d'erreur transmis en appelant l'instance Promise
appelant reject
.
En pratiquethen()
nous ne l'utilisons généralement que pour gérer la situation résolue, c'est-à-dire ne transmettre que la première fonction de rappel. Pour les situations rejected
, catch
() est utilisé pour les gérer uniformément.
let getPromise = () => { return new Promise((resolve, reject) => { // TODO // 处理结果 if (result) { resolve(successObject) } else { reject(error) } }) } getPromise() .then(res => { console.log(res) // TODO }) .catch(err => { //TODO })
L'utilisation de la méthode then()
peut continuer à renvoyer un objet Promise
, qui peut être continuellement transmis à travers return
un nouveau Promise
.
getPromise() .then(res => { //第一层Promise console.log(res) // TODO return getPromise() ) .then(res => { // 第二层Promise console.log(res) // TODO }) .catch(err => { // TODO })
Les autres méthodes couramment utilisées incluent Promise.all()
, Promise.race()
. Utilisé lorsque plusieurs Promise
résultats doivent être attendus. Les deux méthodes reçoivent un tableau d'objets composé de Promise
. Lors de l'utilisation de Promise.all()
, ce n'est que lorsque tous les objets Promise
sont dans l'état resolved Promise.all()
que resolved
. Lorsque Promise.race() n'a besoin que d'un seul objet Promise
pour être resolved
, son statut est résolu.
Plus de façons de lire les documents associés.
Encapsulation de l'interface du mini-programme
Appris les bases de Promise En encapsulant des opérations asynchrones et en utilisant des chaînes Promise, le problème de l'enfer de rappel peut être résolu.
Comme wx.request() est utilisé plus fréquemment, nous encapsulons d'abord wx.request().
/* 可以将公用的方法挂在app.js中 */ request: function(method, url, header, data) { return new Promise((resolve, reject) => { wx.request({ method, url, header, data, success(res) { resolve(res) }, fail(err) { reject(err) } }) }) }
基本框架就这样,我们可以进一步修改,比如请求url的基础路径,添加一些公用的header,针对状态码做一些全局处理等。
request: function(method, url, header = {}, data = {}) { // 启动时可将storage中的令牌挂到app.js let token = app.assess_token if (token) { header["Authorization"] = token } return new Promise((resolve, reject) => { wx.request({ method, url: "https://api.domain.com/v1" + url, header, data, success(res) { // 请求成功 if (res.statusCode === 200) { resolve(res) } // 请求成功无响应体 else if (res.statusCode === 204) { /* 可做一些成功提示, 如调用wx.showToast()、wx.showModal()或自定义弹出层等 */ resolve(res) } // 未认证 else if (res.statusCode === 401) { /* 可做一些错误提示,或者直接跳转至登录页面等 */ reject(res) } else if (res.statusCode == 400) { /* 可做一些错误提示*/ reject(res) } else if (res.statuCode === 403) { /* 无权限错误提示*/ reject(res) } // ...其他状态码处理 }, fail(err) { /* 可做一些全局错误提示,如网络错误等 */ reject(err) } }) }) }
封装之后,举个例子,发送请求就可以修改为
/* 方法体中 */ let app = getApp() app.request("POST", "/auth", {}, { username, password }) .then(res => { // 第一层请求 // TODO 成功处理 return app.request("GET", "/goods", {}, {}) }) .then(res => { // 第二层请求 // TODO 成功处理 // 渲染视图 }) .catch(err => { // TODO 错误处理 })
封装一下其他的微信接口
/* 可以将公用的方法挂在app.js中 */ wxLogin: function() { return new Promise((resovle, reject) => { wx.login({ success(res) { if (res.code) { resovle(res) } else { reject({ message: "登录失败" }) } }, fail(err) { reject(err) } }) }) } getLocation: function() { return new Promise((resolve, reject) => { wx.getLocation({ success(res) { resolve(res) }, fail(err) { reject(err) } }) }) }
对于最初的例子,可以就修改为
/* Page对象的方法 */ getNearby: function() { // 判断是否已认证,可采用wx.checkSession()方案 if (isAuth) { // TODO: 获取业务数据 return } app.wxLogin() .then(res => { // 将code发送给开发者服务器,获取自定义登录态 return app.request("POST", "/auth", {}, { code, res.code }) }) .then(res => { // 保存自定义登录态 setStorage("access_token", res.data.access_token) // TODO: 其他登录成功操作... return app.getLocation() }) .then(({ latitude, longitude }) => { let url = "/nearby?latitude=" + latitude + "&longitude=" + longitude return app.request("GET", url) }) .then(res => { // TODO: 数据处理 let data = res.data // 渲染视图层 this.setData({ data }) }) .catch(err => { // TODO 错误处理 }) }
之后若有需添加新的请求或者其他异步操作,直接在Promise链上操作就行了。
推荐教程:《微信小程序》
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!