Я считаю, что многие разработчики сталкивались с проблемой callback hell. Поскольку API-интерфейсы мини-программ WeChat в основном представляют собой асинхронные операции, основанные на функциях обратного вызова, если вы не используете другие фреймворки или инкапсулированные API-интерфейсы, особенно если вы используете большеwx.request()
, он вскоре столкнется с проблемой ада обратных вызовов, и поддерживать его будет очень больно.
Например
Предполагая, что в настоящее время разрабатывается социальный апплет, одна из функций заключается в том, что пользователь апплета может видеть находящихся поблизости людей после входа в систему.
Предполагая, что используются следующие идеи реализации, мы передаемwx.getLocation()
Получите текущее местоположение пользователя, а затем передайтеwx.request()
Запросить серверные данные. Но перед этим вам необходимо войти в систему. Обратитесь к методу входа, рекомендованному официальной документацией ранее, и вызовитеwx.login()
Получите код и используйте его сноваwx.request()
Запросите сервер разработчика, успешно верните пользовательское состояние входа в систему (обычно в виде access_token или других токенов), а затем используйте пользовательское состояние входа для запроса бизнес-данных.
Для удобства я выложил процесс входа в официальный документ⬇️
После определения идеи начните пробовать кодирование (следующий код не рекомендуется)
/* 以下为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: ...
}
})
}
Появился ад обратного звонка. Волна цигун кода, не говоря уже о других, даже его взгляд будет тошнить.
Однажды встал мудрый продакт-менеджер и сказал, что мы можем добавить ХХХХХ, возможно, вам придется найти место для вложения других интерфейсов WeChat или добавить еще несколькоif else
Бранч, найди место, чтобы поплакать, когда придет время.
решение
В некотором смысле, сегодняшняя бурная фронтенд-экосистема зависит от появления Node и ES6+.
Есть несколько решений для асинхронности после ES6. Один из них - использоватьgenerator/yield
,ноgenerator
Функция на самом деле хлопотно. Другой принятPromise
, относительно просто. ES7 также можно использоватьasync/await
, а по существуasync/await
также на основеPromise
. Представьте нижеPromise
.
Promise
Создание обещаний
Создавать промисы просто,Promise
сам является конструктором. пройти черезnew
Создайте. Параметр конструктора является функцией обратного вызова, а функция обратного вызова имеет два параметра:resolve
а такжеreject
(ручное обслуживание не требуется).resolve
а такжеreject
используется для изменения состояния. О состоянии расскажу позже.
// Promise实例的创建
let p = new Promise((resolve, reject) => {
// TODO
})
Обещания имеют недостаток, что они выполняются, как только они созданы. Так что это обычно обернуто в функцию.
let getPromise = () => {
return new Promise((resolve, reject) => {
// TODO
})
}
Состояние обещания
Экземпляр Promise имеет три состояния:pending
,resolved
а такжеrejected
, экземпляр Promise будет вpending
условие. в функции обратного вызоваresolve
а такжеreject
Он используется для изменения состояния экземпляра Promise. при звонкеresolve
, экземпляр Promise будетpending
сталиresolved
Статус, указывающий на успех. при звонкеreject
, экземпляр Promise будетpending
сталиrejected
Статус, указывающий на отказ.
let getPromise = () => {
return new Promise((resolve, reject) => {
// TODO
// 处理结果
if (result) {
resolve(successObject)
}
else {
reject(error)
}
})
}
Общий метод
Наиболее распространенным методом являетсяthen()
а такжеcatch()
Эти два метода черезthen()
Утилита сквозного доступа может решить проблему ада обратного вызова.
вthen()
Может принимать два параметра, оба являются функциями обратного вызова, первая функция обратного вызова используется для обработкиresolved
Состояние, параметр - это вызов экземпляра Promiseresolve
Переданный объект успеха. Вторая функция обратного вызова используется для обработкиrejected
Состояние, параметр — это вызов вызова экземпляра Promisereject
Переданный объект ошибки.
В реальностиthen()
Обычно мы используем толькоresolved
, то есть передается только первая функция обратного вызова. дляrejected
Ситуация болееcatch()
единая обработка.
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
})
использоватьthen()
Метод может продолжать возвращать объект Promise путемreturn
Новое Обещание, которое можно передавать непрерывно.
getPromise()
.then(res => { //第一层Promise
console.log(res)
// TODO
return getPromise()
)
.then(res => { // 第二层Promise
console.log(res)
// TODO
})
.catch(err => {
// TODO
})
Другие распространенные методы, такие какPromise.all()
,Promise.race()
. Он используется, когда вам нужно дождаться нескольких результатов Promise. Оба метода получают массив объектов, состоящий из промисов. использоватьPromise.all()
, только когда все объекты Promiseresolved
Promise.all()
состояниеresolved
. а такжеPromise.race()
Просто имейте объект Promise какresolved
, его состояниеresolved
.
Дополнительные методы см. в соответствующих документах.
Интерфейс апплета пакета
После изучения основ Promise путем инкапсуляции асинхронных операций проблема ада обратных вызовов может быть решена с помощью цепочки Promise.
потому чтоwx.request()
Частота использования относительно высока, во-первых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-адреса, добавление некоторых общих заголовков, выполнение некоторой глобальной обработки кода состояния и т. д.
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 错误处理
})
Инкапсулируйте другие интерфейсы WeChat
/* 可以将公用的方法挂在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 错误处理
})
}
После этого, если вам нужно добавить новые запросы или другие асинхронные операции, вы можете напрямую работать с цепочкой промисов.