回调函数:
mdn:回调函数 - MDN Web 文档术语表:Web 相关术语的定义 | MDN
被作为实参传入另一函数,并在该外部函数内被调用,用以来完成某些任务的函数,称为回调函数。
| 把一个函数当作参数传递,传递的是函数的定义并不会立即执行,而是在将来特定的时机再去调用,这个函数就叫做回调函数。在定时器setTimeout以及Ajax的请求时都会用到回调函数。 |
|---|
当存在异步任务的代码,不能保证能按照顺序执行,而我们又需要代码顺序执行就回产生回调地狱
setTimeout(function () {//第一层
console.log(1);
setTimeout(function () {//第二程
console.log(2);
setTimeout(function () {//第三层
console.log(3);
}, 1000);
}, 1000);
}, 1000);
这种回调函数的层层嵌套,就叫做回调地狱。回调地狱会造成代码可复用性不强,可阅读性差,可维护性(迭代性差),扩展性差等等问题。
Promise对象
使用语法
Promise对象是一个原生的JavaScript对象,是一种异步编程的解决方案,可以替换掉传统的回调函数。
| 你可以把这个对象当一个容器,里面保存着某个未来才会结束的事件 (通常是一个异步操作)的结果。 |
|---|
从语法上说,Promise是一个对象,从它可以获取异步操作的消息。
Promise有3个状态:pending(初始)、resloved(成功)、rejected(失败)
- Promise 状态发生改变,就会触发.then()里的响应函数处理后续步骤。
- Promise 状态一经改变,不会再变。
- Promise 实例一经创建,执行器立即执行。
创建一个promise对象
Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject,它们是两个函数,又是JavaScript引擎提供,不是自己设置的。
- resolve函数的作用:将Promise对象的状态从“Pending未完成”变成“Resolved成功”
- reject函数的作用:在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
new Promise((resolve,reject)=>{
resolve(); // 操作成功时调用,返回成功后的结果
reject(); // 操作失败时调用,返回错误参数
})
实列方法:then**()、catch()**、finally()
then()
用来接收处理成功时响应的数据,因此与它对应数据是**resolve函数返回的数据。**每一个then方法中的返回值都是一个promise对象
| then()方法有个小问题,每个 then 函数只能访问前一个 then 返回的结果。而解决方法也在下面的技巧中提到 |
|---|
使用demo:
我们来设置一个定时器异步函数
const delay = (ms) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, ms);
});
}
delay(1000)
.then(() => {
console.log(1);
return delay(1000);
})
.then(() => {
console.log(2);
a = "我是变量a"
return { a, c:() => { console.log("我是c变量执行的函数") } };//小技巧
})
.then((res) => {
res.c()
console.log("3", a );
});
console:
// 1
// 2
// 我是c变量执行的函数
// 3 我是变量a
| 上述代码中有个小技巧,then中返回的对象里面可以存当前作用域的变量给下一个then中使用 |
|---|
catch()
用来接收处理失败时响应的数据,因此与它对应数据是reject函数返回的数据
finally()
无论成功与否,都会执行的操作( ES2018 引入)
| Promise的链式编程可以保证代码的执行顺序,前提是每一次在then做完处理后,一定要return一个Promise对象,这样才能在下一次then时接收到数据。 |
|---|
let p = new Promise((resolve,reject)=>{
resolve(); // 操作成功时调用,返回成功后的结果
reject(); // 操作失败时调用,返回错误参数
})
p.then(data => {
// 接收处理成功时响应的数据
}).catch(err => {
// 接收上述所有then方法出错后处理失败时响应的数据
}).finally(()=>{
//无论如何都会执行的方法
})
静态方法:all()、race()、any()、resolve()、reject()
all()
all方法可以完成并发任务, 它接收一个数组,数组的每一项都是一个promise对象,返回一个Promise实例。当数组中所有的promise的状态都达到resolved的时候,all方法的状态就会变成resolved,如果有一个状态变成了rejected,那么all方法的状态就会变成rejected。
race()
race方法和all一样,接受的参数是一个每项都是promise的数组,但是与all不同的是,当最先执行完的事件执行完之后,就直接返回该promise对象的值。如果第一个promise对象状态变成resolved,那自身的状态变成了resolved;反之第一个promise变成rejected,那自身状态就会变成rejected。
any()
它接收一个数组,数组的每一项都是一个promise对象,该方法会返回一个新的 promise,数组内的任意一个 promise 变成了resolved状态,那么由该方法所返回的 promise 就会变成resolved状态。如果数组内的 promise 状态都是rejected,那么该方法所返回的 promise 就会变成rejected状态,
resolve()、reject()
巧妙创建Promise实例
一般情况下,都会使用new Promise()来创建promise对象,但是也可以使用promise.resolve和promise.reject这两个方法:
- Promise.resolve
Promise.resolve(value)的返回值也是一个promise对象,可以对返回值进行.then调用,代码如下:
Promise.resolve(11).then(function(value){
console.log(value); // 打印出11
});
- Promise.reject
Promise.reject 也是new Promise的快捷形式,也创建一个promise对象。代码如下:
Promise.reject(new Error(“我错了!!”));
| Promise虽然摆脱了回调地狱,但是then的链式调⽤也会带来额外的阅读负担,并且Promise传递中间值⾮常麻烦 并且Promise的调试很差,由于没有代码块,你不能在⼀个返回表达式的箭头函数中设置断点,如果你在⼀个.then代码块中使⽤调试器的步进(step-over)功能,调试器并不会进⼊后续的.then代码块,因为调试器只能跟踪同步代码的每⼀步。 所以ES2017推出了新的语法 async/await来更好的解决异步问题 |
|---|
async/await 语法糖
async函数是ES2017中引入的更为高级的异步处理机制,可以让异步的处理变的更加便捷,相当于是promise语法的 “高级写法”。
使用语法
async/await必须在函数中使用,因此我们将代码包进一个函数,并在函数前加上async,这样我们可以在函数中使用await关键字
async用法
const test = async () => {
}
test()
await用法
const test = async () => {
let a = 0
const res = await new Promise((resolve, reject) => {
setTimeout(() => {
a = 1
resolve(a)
}, 1000)
})
console.log(res) //1
}
test()
| await后面是必须是promise对象, 左侧的返回值就是这个promise对象的then方法中回调函数中的value值。换句话说就是,假如是个网络请求,返回的就是得到的数据 |
|---|
解决回调地狱案例
demo1:普通定时器回调函数嵌套
回调地狱
setTimeout(function () {//第一层
console.log(1);
setTimeout(function () {//第二程
console.log(2);
setTimeout(function () {//第三层
console.log(3);
}, 1000);
}, 1000);
}, 1000);
promis
const delay = (ms) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, ms);
});
}
delay(1000)
.then(() => {
console.log(1);
return delay(1000);
})
.then(() => {
console.log(2);
return delay(1000);
})
.then(() => {
console.log(3);
});
async/await
const delay = (ms) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, ms);
});
}
const run = async () => {
await delay(1000);
console.log(1);
await delay(1000);
console.log(2);
await delay(1000);
console.log(3);
};
run();
demo2:微信运动登录回调函数嵌套获取当前用户微信运动函数
回调地狱
wx.login({
success (res) {
if (res.code) {
let code = res.code //根据官方文档来说需要用户code辅助解密(与本案例无关)
//发起网络请求
wx.getWeRunData({
success: function(runRes) {
// 获取微信运动加密信息和微信运动辅助解密向量
let encryptedData = runRes.encryptedData;
let iv = runRes.iv;
this.getLoadWeChatStep({//解密api(随便写的来自后端的接口)
code:code, //根据官方文档来说需要用户code辅助解密(与本案例无关)
encryptedData:encryptedData,
iv:iv,
success: function(res) {
//解密成功
},
fail: function() {
//解密成功
}});
},
fail: function() {
// 未开通微信运动
}
});
} else {
console.log('登录失败!' + res.errMsg)
}
}
})
promis
// 首先调用 wx.login() 获取 code
wx.login()
.then(code => {
// 使用获取到的 code 继续请求微信运动数据
return wx.getWeRunData().then(runRes => {
// 解构赋值,获取加密数据和初始向量
const { encryptedData, iv } = runRes;
// 将 code, encryptedData, iv 传递给解密函数
return { code, encryptedData, iv };
});
})
.then(({ code, encryptedData, iv }) => {
// 调用解密函数,这里假设 getLoadWeChatStep 已经定义在合适的上下文中
return this.getLoadWeChatStep(code, encryptedData, iv);
})
.then(decryptedData => {
// 解密成功后的处理
console.log('解密成功', decryptedData);
})
.catch(error => {
// 错误处理
console.error(error);
});
async/await
const processWeRunData = async () => {
try {
const code = await wx.login();
const runRes = await wx.getWeRunData();
const encryptedData = runRes.encryptedData;
const iv = runRes.iv;
const decryptedData = await this.getLoadWeChatStep(code, encryptedData, iv);
// 解密成功
} catch (error) {
console.error(error);
}
};
processWeRunData();
相关文章分享:
uniapp+vue2如何获取微信运动数据
参考:
async 函数 - JavaScript | MDN
await - JavaScript | MDN
回调函数 - MDN Web 文档术语表:Web 相关术语的定义 | MDN
回调地狱及Promise、async和await_await async 回调地狱-CSDN博客
什么是回调地狱,如何用Promise解决回调地狱,看完这篇你就明白了。 - 掘金 【JavaScript】【回调】回调函数 && 回调地狱 - 掘金
js Promise与async/await用法详解 - 掘金
优雅的处理async/await错误 - 掘金
如何解决回调地狱?教你简单粗暴解决问题 - 掘金