用自己的话讲有趣的知识。
大家好,我是梅巴哥er
。本篇讲一些回调函数、promise相关的知识。
以后遇到promise相关的知识,也会在这篇博客里更新。涉及到的讲解,都是个人的理解,如有不当之处,欢迎评论区指出。会及时回复和更正。
说到promise,就不得不先说说回调函数。
// 先看个栗子
function a(m) {
console.log(m)
}
function b(callback) {
var n = 1
callback(n)
}
b(a) // 输出 1
// 可以看出:
// 回调函数a被当做实参传入函数b
// 在执行函数b时,回调函数a被调用
上个栗子是同步执行的函数,即a在b调用的时候,是立即执行的,
不需要等待。
// 那么,我们再来看个栗子
function callback() {
console.log(1)
}
setTimeout(callback, 1000)
// 先执行外层的setTimeout函数,然后调用callback函数。
// 这里的callback就是一个回调函数。
// 回调函数在另一个函数里充当参数
很明显,这个栗子是异步回调,在执行callback回调函数前,是需要等待的。
也就是说,执行回调函数有同步的,也有异步的。
在实际应用中,最多的是在异步中的应用。
从上面的例子可以看出,
我们先来看个例子:
function a(num, callback) {
setTimeout(() => {
console.log(num)
callback() // 回调函数
}, 1000)
}
a(1, function () { // 无限调用
a(2, function() {
a(3, function () {
a(4, function () {
a(5, function () {
console.log('多少层了')
})
})
})
})
})
// 可以无限写下去。。
从上面例子可以看出,
我们中国有句老话,某个人要是做了罪大恶极的事情,都会被打入18层地狱!
看上面栗子的函数,一层一层往下去,像不像地狱???
所以,这种回调,就叫回调地狱
!
然而,我们平时的应用,又经常需要异步调用,甚至还套娃调用,该怎么办呢?
现在,我们把上面的代码改造一下:
function a(num) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(num)
resolve() // 状态成功,执行then里面的函数
// reject() // 状态失败,执行catch里面的函数
}, 1000)
})
}
a(1)
.then(() => a(2)) // then函数里返回的仍然是一个promise对象
.then(() => a(3))
.then(() => a(4))
.then(() => a(5))
.then(() => console.log('多少层了'))
.catch(error => console.log(error))
// resolve取代了回调函数
// 当状态成功时,then指向的是回调函数,一个新的promise对象
现在再看,是不是更清晰了?
你看着一层一层的,很清晰美观,跟千层饼似的,看着就流口水…真香!
看了上面的栗子,你是不是想问:
这是啥意思啊?
那个promise是啥?
怎么还有一堆.then()是干啥的?
resolve、reject怎么还有状态?
用了promise怎么就突然一层一层的清晰了啊?
Promise是什么
说起从上面的例子,我们不难看出:
// 如上面的栗子
function a(num) {
return new Promise((resolve, reject) => {
...
}
}
// 函数a返回(return)了一个对象new Promise()
// 以后我们想用Promise了,就返回或声明一个Promise即可
// Promise通过.then绑定了回调函数(then里面就是回调函数了)
// resolve状态下,执行了then里面的回调函数
// 和回调地狱里的栗子a(num, callback){...}相比,
// Promise栗子的a函数的形参里,
// 并没有写callback回调函数了,因为回调函数已经通过promise绑定了
// .then可以添加多个,按照顺序依次执行,直至回调结束
// 这种多个then(和.catch)的形式,叫链式调用
我们再来看个栗子:
let promise = new Promise((resolve, reject) => {
console.log(1)
// resolve() // 这个状态才会执行then
reject() // 这个状态才会执行catch
// catch后的then,不管什么状态,都会执行
})
promise
.then(() => console.log(2))
.catch(() => console.log(3))
.then(() => console.log(4))
// 输出:1 3 4
// 可以看出:
// 当状态是reject时,会执行.catch里的函数。
// .catch前的.then不会执行
// 而.catch后面的.then不受影响,会继续执行
这只是个人的一种表述习惯。
其实,这也是在说Promise的状态。
Promise有三种状态且必定处于其中一种状态:
当操作成功时(异步任务顺利完成且返回结果值):
就会执行.then的回调函数(即调用 resolve 函数)
当操作失败时(异步任务失败且返回失败原因):
就会执行.catch的函数(即调用reject 函数)
// example1
let pro = new Promise((resolve, reject) => {
setTimeout(
() => resolve('成功') // 异步代码执行成功,调用resolve函数
, 1000)
})
pro
.then(message => console.log('good!' + message + '了!'))
// message的值,是上面调用resolve(..)函数传入的值
// 相当于这样:
// function resolve(message) {console.log('good!' + message + '了!')}
// 这个值不一定是字符串,还可以是别的。
// 比如 resolve(data)
// example2
let pro = new Promise((resolve, reject) => {
setTimeout(
() => reject('failed...') // 异步代码执行失败,调用reject函数
, 1000)
})
pro
.then(message => console.log('good!' + message + '了!'))
.catch(error => console.log(error))
.then(() => console.log('end!'))
// 输出:
// failed... end!
// reject('failed...')就相当于:
// function reject(error) {console.log(error)}
// 再调用reject函数: reject('failed...')
既然说了ES6的Promise,那就不得不提ES7的async/await。因为async就是为了简化Promise而出现的。
为了更好的理解,我现在对Promise例子的example1代码进行改造:
// example1改造后
let pro= () => new Promise((resolve, reject) => {
setTimeout(
() => resolve('成功') // 异步代码执行成功,调用resolve函数
, 1000)
})
async function asyncExample() {
console.log(1)
const result = await pro()
console.log(result)
}
asyncExample()
// 输出: 1 成功
let pro = new Promise(..)
,改造后怎么变成let pro= () => new Promise(..)
?从例子的改造,我们可以得知:
async function pro(){..}
await pro()
const result = await pro()
,这里的result就是一个promise对象await pro()
就相当于.then((param) => {return param})
以上。