001 Promise

7/17/2021 Javascript

promise 技术方案的出现是为了解决原来的回调地狱,举例我们需要在淘宝买一样东西那么流程如下:

  1. 查找商品
  2. 找到了进行购买生成订单
  3. 付款
  4. 付款成功

那么用ajax请求写出来会如何呢

url: "/api/searchGoods?name="iphone""
success: function (res) {
	if (res.code === 200) { // 找到了
		$.ajax({
      url: "/api/buy?id="88"", // 去生成订单
      success: function (res) {
      	if (res.code === 200) { // 生成订单合法
          $.ajax({
            url: 'api/pay?billid="123"', // 付款
            success: function (res) {
              if (res.code === 200) { // 付款成功
                alert('付款成功')
              }
            }
          })
        }
    	}
    })
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

怎么样是不是晕了,如果这个流程需要10个步骤或者20个步骤呢?或者我们可以将成功后的回调函数写到函数体外,用函数名来进行调用,但是并没有什么作用,当我们的大脑进行线性思考的时候不得不在各个函数之间反复横跳。且 如果中间某一步出错他也并不会移动到一个处理错误的函数上。

那么就需要promise登场了,promise 是一个对象,promise 有三种状态 “pending”、“fulfiled”、“rejected”,状态一旦改变就不会再变,一旦new promise 它会立即执行,promise接收一个函数作为参数,函数需要传入两个值(res, rej)(由promise提供,用该改变promise对象中的值)

const p = new promise((res,rej) => {
	...........
})
1
2
3

意思就是 promise 会给这个函数提供一个res和rej函数用来改变对象的值,当请求完成即可调用对应函数修改对象,后续就可以使用then方法获取对应值。我们将上面的ajax用promise进行优化

function req (url) {
  return new Promise((res, rej) => {
	$.ajax({
		url: url,
    success: function (data) {
    	if (data.code === 200) {
        res(data); // 此时res方便就改变了promise对象中的value值
      } else {
        rej(data.msg) // 如果失败则改变promise对象的error值
      }
  }
	})
})
}
req("/api/searchGoods?name="iphone"").then(value => {
  return req("/api/buy?id="88"")
}, error => console.log(error)).then(value => {
  return req('api/pay?billid="123"')
}, error => console.log(error)).then(value => {
  alert('付款成功')
}, error => console.log(error)).catch(error => {
  console.log(error)
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

最后的这个catch是用来接收异常的,一旦有哪一步出错都会被该方法接收,避免了报错卡死

简单实现promise

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {
  constructor(executor) {
    try{
      executor(this.resolve, this.reject)
    } catch(error) {
      this.reject(error)
    }
  }
  status = PENDING
  value = null
  reason = null
  onFulfilledCallbacks = []
  onRejectedCallbacks = []
  resolve = (value) => {
    if (this.status === PENDING) {
      this.status = FULFILLED
      this.value = value
      while (this.onFulfilledCallbacks.length) {
        this.onFulfilledCallbacks.shift()(value)
      }
    }
  }
  reject = (reason) => {
    if (this.status === PENDING) {
      this.status = REJECTED
      this.reason = reason
      while (this.onRejectedCallbacks.length) {
        this.onRejectedCallbacks.shift()(reason)
      }
    }
  }
  then(onFulfilled, onRejected) {
    const promise2 = new MyPromise((resolve, reject, setName) => {
      setName('promise2');
      if (this.status === FULFILLED) {
        queueMicrotask(() => {
          const x = onFulfilled(this.value)
          resolvePromise(promise2, x, resolve, reject);
        })
      } else if (this.status === REJECTED) {
        onRejected(this.reason)
      } else if (this.status === PENDING) {
        this.onFulfilledCallbacks.push(onFulfilled)
        this.onRejectedCallbacks.push(onRejected)
      }
    })
    return promise2
  }
}

function resolvePromise(promise2,x, resolve, reject) {
  if (promise2 === x) {
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  }
  if (x instanceof MyPromise) {
    x.then(resolve, reject)
  } else {
    resolve(x)
  }
}

export default MyPromise
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
Last Updated: 8/4/2021, 6:25:04 PM