【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()参数,这个函数分别有 resolvereject两个参数,顾名思义,一个代表解决,一个代表拒绝。然后在 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()这种写法的话,可以将它改为 asyncawait 形式的方法。

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.')
  }
})()
posted @ 2020-09-30 21:16    阅读(162)  评论(0编辑  收藏  举报