【JS】快速上手Promise、async、await
快速上手Promise、async、await
round 1
假如你打电话给 Alex,就定义一个函数,名为 callAlex()
:
function callAlex() {
console.log('calling alex...')
}
然后假设 3 秒之后 Alex 才接听,表示连接上了,在 setTimeout
函数里执行 connected()
:
function callAlex() {
console.log('calling alex...')
setTimeout(() => {
connected()
}, 3000)
}
但是,Alex 也可能不会接听,所以给callAlex()
加入参数,命名为 success;这样的话,就判断一下,如果 success 的话,就执行connected()
,否则,执行disconnected()
:
function callAlex(success) {
console.log('calling alex...')
setTimeout(() => {
if (success) {
connected()
} else {
disconnected()
}
}, 3000)
}
function connected() {
console.log('Alex picked up the phone.')
}
function disconnected() {
console.log('Alex rejected the call.')
}
callAlex(true)
输出:
"calling alex..."
"Alex picked up the phone."
如果调用参数改为 false,则是 Alex 拒绝了电话。
可以看到,这样写代码,耦合度太高了,connected()
和disconnected()
这两个函数的名称是固定了,而且是直接写在了 callAlex()
里面。
round 2
为了将这两个函数解耦,需要再给callAlex()
添加两个参数,connectedCallback 和 disconnectedCallback,分别用来代替原来的connected()
和disconnected()
,由于新添加的两个参数都为函数名,所以判断类型再是否执行:
function callAlex(success, connectedCallback, disconnectedCallback) {
console.log('calling alex...')
setTimeout(() => {
if (success) {
(typeof connectedCallback === 'function') && connectedCallback()
} else {
(typeof disconnectedCallback === 'function') && disconnectedCallback()
}
}, 3000)
}
然后,在调用 callAlex()
函数时,给第二、第三个参数都传入一个匿名函数:
callAlex(true, () => {
console.log('Alex picked up the phone.')
}, () => {
console.log('Alex rejected the call.')
})
执行后,逻辑跟之前是一样,只是改变了一下写法,虽然代码比之前解耦了一些,但是可读性变得更差了。
接下来就将它改为用 Promise 的写法。
round 3
将 callAlex()
刚才添加的两个函数参数移除,其 body 里原先的内容被包含在一个 Promise 的容器中。传入一个函数作为 Promise()
参数,这个函数分别有 resolve
和reject
两个参数,顾名思义,一个代表解决,一个代表拒绝。然后在 success 为 true 的情况下执行 resolve()
,否则执行 reject()
。
修改后如下:
function callAlex(success) {
return new Promise((resolve, reject) => {
console.log('calling alex...')
setTimeout(() => {
if (success) {
resolve()
} else {
reject()
}
}, 3000)
})
}
修改一下调用时的callAlex()
,只保留 success 参数,然后在其后面接上 .then()
,如果 Alex 接听了电话(执行resolve
),就会在这个 then 里面处理;如果 Alex 拒绝了电话(执行reject
),就会在 catch 里面处理,.catch()
可以接在callAlex()
后面或者.then()
后面:
callAlex(true)
.then(() => {
console.log('Alex picked up the phone.')
})
.catch(() => {
console.log('Alex rejected the call.')
})
执行这些代码后,你会发现,当resolve()
执行时,.then()
就会执行;当reject()
执行时,.catch()
就会执行。
这样看上去呢,代码的可读性就好了很多。
round 4
如果你不喜欢 .then()
、.catch()
这种写法的话,可以将它改为 async
、await
形式的方法。
callAlex()
这个函数不用改,准备一个 try catch
的代码块,try 里面就是 resolve()
的结果,catch 里面就是reject()
的结果。使用await callAlex(true)
这种方式去调用:
try {
await callAlex(true)
console.log('Alex picked up the phone.') // 成功
} catch {
console.log('Alex rejected the call.') // 失败
}
上面代码的意思是,在这个 try
代码块里,await
等待 callAlex()
的 Promise
结果。如果是 resolve()
的话,就继续执行后续代码;如果是reject()
的话,就执行 catch
里面的代码。
如果有await
这个关键词的代码,只能在一个定义为async
的函数内执行:
async function action() {
await callAlex(true)
try {
console.log('Alex picked up the phone.')
} catch {
console.log('Alex rejected the call.')
}
}
action()
所以这里我定义了一个action()
函数,然后在这个函数前面加上async
关键词,最后执行action()
即可。
当然,你也可以将它改为IIFE(即时执行方法)
的写法:
(async () => {
await callAlex(true)
try {
console.log('Alex picked up the phone.')
} catch {
console.log('Alex rejected the call.')
}
})()