规范使用 Promise.then

日常开发中经常碰见需要连续执行多个异步操作,下一个异步操作需要从上一个异步操作返回的结果中获取数据。在这种场景下就适合使用 Promise 的链式调用。

doSomething().then(function(result) {
  return doSomethingElse(result);
})
.then(function(newResult) {
  return doThirdThing(newResult);
})
.then(function(finalResult) {
  console.log('Got the final result: ' + finalResult);
})
.catch(failureCallback);
1
2
3
4
5
6
7
8
9
10

Promise.then 返回一个新的 promise 并带有上个 then 回调函数中返回的结果。

错误的使用方式

const p1 = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('111')
    }, 5000)
  })
}

const p2 = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('222')
    }, 1000)
  })
}

const p3 = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('333')
    }, 1000)
  })
}
// 错误使用嵌套 promise
p1().then(p1Res => {
  console.log(p1Res)
  p2().then(p2Res => {
    console.log(p2Res)
    p3().then(p3Res => {
      console.log(p3Res)
    })
  })
})
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

以上使用方式中存在几点错误:

  1. 在一个 then 的回调函数中没有返回,而是嵌套 promise
  2. 没有发挥 promise 链式调用的优点,与传统回调方式无异,存在回调地狱
  3. 没有使用 catch 进行错误处理

then 链式调用

正确的使用方式

p1().then(res => {
	console.log(res)
	return p2()
}).then(res => {
	console.log(res)
	return p3()
}).then(res => {
	console.log(res)
}).catch(err => {
  console.log(err)
})
1
2
3
4
5
6
7
8
9
10
11

时序组合

将多个异步操作按照先后顺序组成数组,用递归方法执行

[p1, p2, p3].reduce((p, f) => p.then(res => {
	console.log(res)
	return f()
}), Promise.resolve())
.then(res => {console.log(res)})
.catch(err => {console.log(err)})

// 相当于
Promise.resolve()
.then(res => p1())
.then(res => p2())
.then(res => p3())
.catch(err => console.log(err))
1
2
3
4
5
6
7
8
9
10
11
12
13

async/await

使用 async/await 可以更简洁方便地执行时序异步操作

const p1Result = await p1()
const p2Result = await p2()
const p3Result = await p3()
1
2
3

参见