async 和 await

2022/6/7 JavaScriptasyncawait

# 异步请求的处理方式

假设有一个需求,需要多次请求

需求:
1: url:okarin -> res: okarin
2: url:res+ 'aaa' ->okarinaaa
3: url:res+ 'bbb' ->okarinaaabbb
4: url:res+ 'ccc' ->okarinaaabbbccc
1
2
3
4
5

请求的 Promise

function requestData(url) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(url)
    }, 1000)
  })
}
1
2
3
4
5
6
7

# 方案一:多次回调(回调地狱)

requestData("okarin").then((res) => {
  requestData(res + "aaa").then((res) => {
    requestData(res + "bbb").then((res) => {
      requestData(res + "ccc").then((res) => {
        console.log(res)
      })
    })
  })
})
1
2
3
4
5
6
7
8
9

会形成回调地狱

# 方案二:Promise 返回值

requestData("okarin")
  .then((res) => {
    return requestData(res + "aaa")
  })
  .then((res) => {
    return requestData(res + "bbb")
  })
  .then((res) => {
    return requestData(res + "ccc")
  })
  .then((res) => {
    console.log(res)
  })
1
2
3
4
5
6
7
8
9
10
11
12
13

可读性和可维护性差

# 方案三:Promise + Generator

function* getData() {
  const res1 = yield requestData("okarin")
  const res2 = yield requestData(res1 + "aaa")
  const res3 = yield requestData(res2 + "bbb")
  const res4 = yield requestData(res3 + "ccc")
  console.log(res4)
}

const generator = getData()
generator.next().value.then((res) => {
  generator.next(res).value.then((res) => {
    generator.next(res).value.then((res) => {
      generator.next(res).value.then((res) => {
        generator.next(res)
      })
    })
  })
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 使用递归函数优化

function execGenerator(genFn) {
  const generator = genFn()
  function exec(res) {
    const result = generator.next(res)
    if (result.done) {
      return result.value
    }
    result.value.then((res) => {
      exec(res)
    })
  }
  exec()
}

execGenerator(getData)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 方案四:async/await

async function getData() {
  const res1 = await requestData("okarin")
  const res2 = await requestData(res1 + "aaa")
  const res3 = await requestData(res2 + "bbb")
  const res4 = await requestData(res3 + "ccc")
  console.log(res4)
}

getData()
1
2
3
4
5
6
7
8
9

async/await 的原理就是使用 Promise 和生成器。

# async 异步函数

async 关键字用于声明一个异步函数

async function foo() {}

const foo = async () => {}
1
2
3

# 异步函数的执行流程

默认情况下 async 函数跟普通函数的执行流程没有区别,会被同步执行。

async function foo() {
  console.log("foo function start")
  console.log("hello world")
  console.log("foo function end")
}

console.log("111111")
foo()
console.log("222222")

//111111
// foo function start
// hello world
// foo function end
// 222222
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 异步函数的返回值

异步函数也可以有返回值,但是会被包裹到 Promise.resolve 中。

async function foo() {
  console.log("foo function start")
  console.log("hello world")
  console.log("foo function end")
  return "aaa"
}

const promise = foo()
promise.then((res) => {
  console.log(res)
})

//foo function start
//hello world
//foo function end
//aaa
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

如果返回值是一个对象并实现了 thenable,那么会由对象的 then 方法来决定。

async function foo() {
  console.log("foo function start")
  console.log("hello world")
  console.log("foo function end")
  return {
    then: function (resolve, reject) {
      resolve("bbb")
    },
  }
}

const promise = foo()
promise.then((res) => {
  console.log(res)
})

//foo function start
//hello world
//foo function end
//bbb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

如果返回值是一个 Promise,Promise.resolve 的状态会由返回的 Promise 决定。

async function foo() {
  console.log("foo function start")
  console.log("hello world")
  console.log("foo function end")

  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("ccc")
    }, 2000)
  })
}
const promise = foo()
promise.then((res) => {
  console.log(res)
})

//foo function start
//hello world
//foo function end
//ccc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 异步函数的异常

如果在 async 中抛出了异常,那么程序它并不会像普通函数一样报错,而是会作为 Promise 的 reject 来传递;

async function foo() {
  console.log("foo function start")
  console.log("hello world")

  throw new Error("err message!")
  //异步函数内部后续代码
  console.log("foo function end")
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("ccc")
    }, 2000)
  })
}

console.log("111111")
const promise = foo()
promise
  .then((res) => {
    console.log(res)
  })
  .catch((err) => {
    console.log("Error:", err)
  })

//后续代码
console.log("222222")
console.log("333333")

// 111111
// foo function start
// hello world
// 222222
// 333333
// Error: Error: err message! ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

可以发现,异步请求中抛出的异常,会中断异步函数的执行,但是不会影响函数外部后续代码的执行

# await 关键字

async 函数内部可以使用 await 关键字,而普通函数中是不可以的。

await 关键字的特点:

  • 通常使用 await 是后面会跟上一个表达式,这个表达式会返回一个 Promise
  • 那么 await 会等到 Promise 的状态变成 fulfilled 状态,之后继续执行异步函数;如果 await 后面是一个普通的值,那么会直接返回这个值;
  • 如果 await 后面是一个 thenable 的对象,那么会根据对象的 then 方法调用来决定后续的值;

await 后接 Promise

function requestData(url) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(url)
    }, 1000)
  })
}

async function foo() {
  const res = await requestData("okarin")
  console.log(res)
  console.log("111111111111111")
}

foo()

//okarin
//111111111111111
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

await 后接 thenable

async function foo() {
  const res = await {
    then: function (resolve, reject) {
      setTimeout(() => {
        resolve("abc")
      }, 2000)
    },
  }
  console.log(res)
  console.log("111111111111111")
}

foo()

//abc
//111111111111111
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

await 后 Promise 的状态如果变成 rejected,会把 reject 的值作为整个函数返回的 Promise 的值,那么需要使用 catch 回调。

function requestData(url) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      //   resolve(url)
      reject("error message!")
    }, 1000)
  })
}

async function foo() {
  console.log("111111111111111")
  const res = await requestData("okarin")
  console.log(res)
  console.log("222222222222222")
}

foo().catch((err) => {
  console.log("Error:", err)
})

//111111111111111
//Error: error message!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# await 执行流程

function requestData(url) {
  console.log("222222222222222")
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(url)
      // reject("error message!")
    }, 1000)
  })
}

async function foo() {
  console.log("111111111111111")
  const res = await requestData("okarin")
  console.log(res)
  console.log("333333333333333")
}

console.log("script start")
foo()
console.log("script end")

// script start
// 111111111111111
// 222222222222222
// script end
// okarin
// 333333333333333
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Last Updated: 2022/6/7上午11:56:24