# 手写promsie

# 基于Promsie封装的图片加载函数

来看一个简单的基于Promsie封装的图片加载函数

function loadImageAsync(url) {
  return new Promise((resolve,reject) => {
    // 创建图片对象
    const image = new Image();
    image.src = url;
    image.onLoad = function() {
      // 返回image对象
      resolve(image);
    } 
    image.onerror = function() {
      reject(new Error('Could not load image at ' + url));
    }
  })
} 
// 封装的函数返回一个Promsie包含then方法,结果 
loadImageAsync("./loadImge.jpg").then(image => document.body.appendChild(image))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 基于Promise对象实现Ajax操作的例子

const getJSON = function(url) {
  const promsie = new Promsie((resolve, reject) => {
    const handle = function() {
      if (this.readyState !== 4) {
        return;
      }
      if (this.status === 200) {
        resolve(this.response)
      } else {
        reject(new Error(this.statusText)
      }
    }
    const client = new XMLHttpRequest()
    client.open("GET",url)
    client.onreadystatechange = handler;
    client.responseType = "json";
    client.setRequestHeader("Accept", "application/json");
    client.send();
  })
  return promsie
}

getJSON("/posts.json").then(function(json){
  console.log('Contents: ' + json);
},function(error) {
  console.error('出错了', error);
})
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

来看一下promise的简单示例。

new Promise(()=>{
  console.log("executor")
})
console.log("ok")
// 控制行输出 =>  executor ok
1
2
3
4
5
  • Promsie可以使用new关键词调用,我们可以把Promise看成是一个类,在现阶段我们不考虑兼容性问题。

  • 当使用Promise的时候,会传入一个 executor 函数,这个函数会立即执行。

  • 这个executor函数可以传递两个参数,这两个参数也是函数,一个是resolve 另一个是reject,这两个函数的执行可以改变promise的状态。

  • promise中有三种状态:

    • 成功态
    • 失败态
    • 中间状态:pending
  • 默认情况下promsie处于中间状态。

  • 使用new操作符操作promsie之后,会生成一个实例,每一个实例都拥有一个then方法。

  • then方法接收两个函数作为参数,一个是成功回调,一个是失败回调。根据上面的描述信息,我们补充代码:

let promise = new Promise((resolve, reject) => {
  console.log("executor")
})
console.log("ok")

// 每个实例都用拥有一个then方法
promise.then(()=>{
  console.log("success");
},()=>{
  console.log("failed");
})
// 控制行输出 =>  executor ok
1
2
3
4
5
6
7
8
9
10
11
12

通过打印输出可以看到,虽然我们在then方法中写了success 和 failed 但是既没有走成功回调,也没有走失败回调。

这是因为默认状态下promsie处于的是等待状态。我们想要成功还是失败,需要主动调用resolve或者调用reject告诉promsie。如果调用resolve函数promise转变为成功状态,如果调用reject函数promise就转变为失败状态。

# 第一版代码

我们尝试手写一版Promise,使用 commonjs 规范,之后使用Promsie的时候就导入自己手写的这个 Promise 类。

// promsie 有三种状态,分别是 等待、成功、失败 我们用常量来表示。
const PENDING = "PENDING"
const FULFILLED = "FULFILLED"
const REJECTED = "REJECTED"
// Promsie 内部是一个类,我们这里使用 class 来实现
class Promise {
  // 构造函数的入参就是自执行函数 executor
  constructor(executor) {
    this.status = PENDING // promise默认的状态
    this.value = undefined // 成功的值
    this.reason = undefined // 失败的原因
    // 成功resolve函数
    const resolve = (value) => {
      // 只有在 PENDING 状态下 才能 修改状态
      if (this.status === PENDING) {
        this.value = value
        this.status = FULFILLED // 修改状态
      }
    }
    // 失败的reject函数
    const reject = (reason) => {
      // 只有在 PENDING 状态下 才能 修改状态
      if (this.status === PENDING) {
        this.reason = reason
        this.status = REJECTED // 修改状态
      }
    }
    // 这里使用try catch包裹是因为立即执行函数执行的时候如果抛出异常,直接走reject函数。
    try {
      executor(resolve, reject)
    } catch (e) {
      reject(e)
    }
  }
  // then方法的两个回调函数,都是使用者传递的
  then(onFulfilled, onRejected) {
    // onFulfilled, onRejected
    if (this.status == FULFILLED) {
      // 成功调用成功方法,并传入成功的值
      onFulfilled(this.value)
    }
    if (this.status === REJECTED) {
      // 失败调用失败方法  并传入失败的原因
      onRejected(this.reason)
    }
  }
}

module.exports = Promise
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

我们在实际的使用场景中,一般在executor函数中传入的都是异步任务,类似于下面这样。




 
 
 











// 使用自己的promsie
let Promise = require('./promise')
let promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("executor")
  }, 1000)
})

console.log("ok")

promise.then(()=>{
  console.log("success");
},()=>{
  console.log("failed");
})
// => 控制台打印 ok 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

执行上述代码发现,如果我们在executor函数中传入的是一个异步任务,虽然调用了resolve方法,但是并没有走实例的then方法的成功回调,这是为什么呢?

因为默认promise处于pending状态,当我们用定时器延时去触发resolve的时候,下面的then方法已经执行完毕了,但是我们自己手写的这一版本promsie中并没有处理pending状态。

# 处理pending状态

我们可以这样处理,准备两个数组,分别用于存放成功回调函数失败回调函数,当我们真正触发 resolve 或者 reject 的时候遍历数组执行其中的回调函数。

这种实现的思路其实是发布订阅的模式。先订阅好所有的成功回调和失败回调,然后在触发 resolvereject 的时候执行发布。












 
 






 







 










 
 
 
 
 
 
 
 
 
 














// promsie 有三种状态,分别是 等待  成功  失败 我们用常量来表示。
const PENDING = "PENDING"
const FULFILLED = "FULFILLED"
const REJECTED = "REJECTED"
// Promsie 内部是一个类,我们这里使用 class 来实现
class Promise {
  // 构造函数的入参就是自执行函数 executor
  constructor(executor) {
    this.status = PENDING // promise默认的状态
    this.value = undefined // 成功的原因
    this.reason = undefined // 失败的原因
    this.onResolvedCallbacks = []; // 存放成功的回调方法
    this.onRejectedCallbacks = []; // 存放失败的回调方法
    // 成功resolve函数
    const resolve = (value) => {
      // 只有在 PENDING 状态下 才能 修改状态
      if (this.status === PENDING) {
        this.value = value
        this.status = FULFILLED // 修改状态
        this.onResolveCallbacks.forEach((fn) => fn())
      }
    }
    // 失败的reject函数
    const reject = (reason) => {
      if (this.status === PENDING) {
        this.reason = reason
        this.status = REJECTED // 修改状态
        this.onRejecteCallbacks.forEach((fn) => fn())
      }
    }
    try {
      executor(resolve, reject)
    } catch (e) {
      // 立即执行函数执行的时候如果抛出异常,直接走reject函数。
      reject(e)
    }
  }
  then(onFulfilled, onRejected) {
    if(this.status == PENDING){
      this.onResolvedCallbacks.push(() => { 
        // todo...
        onFulfilled(this.value);
      });
      this.onRejectedCallbacks.push(() => {
        // todo...
        onRejected(this.reason);
      });
    }
    // onFulfilled, onRejected
    if (this.status == FULFILLED) {
      // 成功调用成功方法,并传入成功的值
      onFulfilled(this.value)
    }
    if (this.status === REJECTED) {
      // 失败调用失败方法  并传入失败的原因
      onRejected(this.reason)
    }
  }
}

module.exports = Promise
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

# promsie 到底帮助我们解决了什么问题

promsie 到底在我们的日常开发中解决了什么问题?

  • 解决了回调地域的问题。
  • 解决了同步并发的问题。

假设我们有一个场景:存在两个文件 a.txtb.txt

a.txt中的内容是一个字符串'b.txt', b.txt中的内容是 'b',我们希望先从 a.txt 中读取文件内容,然后再操作输出b.txt的内容。我们在不使用promsie的时候可能会写出如下的代码:

fs.readFile('./a.txt','utf8',function (err, data) {
  if(err) return console.log(err)
  fs.readFile('./b.txt','utf8',(err,data) => {
    if(err) return console.log(err)
    console.log(data); // => 输出b
  })
})
1
2
3
4
5
6
7

这种写法会有回调嵌套的问题,就是不断地在上一个函数的回调中写逻辑,并且每一层都要写错误捕获逻辑。这样层级多了之后,代码就会变得难以维护。

基于此我们可以尝试使用promise封装一个方法。

const fs = require('fs');
// 入参是文件的 路径 和 文件编码
function readFile(filePath, encoding) {
  // 返回一个 promsie实例
  return new Promise((resolve,reject) => {
    // 传递给原生的fs.readFile 方法一个回调函数
    fs.readFile(filePath,encoding, (err,data) => { 
      // 拿到文件读取结果之后,通过resolve 或者 reject 将结果返回。
      if(err) return reject(err);
      resolve(data);
    })
  })
}
1
2
3
4
5
6
7
8
9
10
11
12
13

如上述代码所示,我们封装了一个 readFile 方法,返回了一个 promise 实例,在自执行函数中调用原生的操作文件的方法,在文件读取结束之后,将结果通过resove 或者 reject 返回出来。

promise中给我们提供的then方法能够解决这种不断嵌套的问题。

# then方法

promsie的链式调用是个很有意思的现象,当promise实例调用then方法后,还会返回一个新的promise,新的promsie拥有有自己的状态,如果我们用过jquery这个库的时候会知道,也会有链式调用的场景,但是返回的是this。

那我们不禁有一个疑问,promsie为什么不能返回自身呢?原因就在于,promise的状态一旦改变之后,就不能再改回去了,否则状态就乱了,因此,需要返回一个新的promsie。

# 情况1:then中的方法返回值是一个普通值

所谓的普通值,代表不是promise实例的场景,比如基本类型的数字或者字符串。这种情况下,返回值会作为外层下一次then的成功结果

还是拿刚才我们基于promsie封装的函数readFile来说

readFile("./a.txt", "utf8").then((value)=>{
  reutrn 1
},(err)=>{
  console.log('fail',err)
}).then((data)=>{
  console.log('s',data) // 在这里输出 => s 1
},(err)=>{
  console.log('f',err)
})
1
2
3
4
5
6
7
8
9

# 情况2:then方法执行出错

比如 throw new Error("失败") 无论上一次是成功还是失败,只要返回的是普通值,都会执行下一次的then的成功。

readFile("./a.txt", "utf8").then((value)=>{
  throw new Error("抛出异常")
},(err) => {
  console.log('fail',err) // 在这里输出 抛出异常 
}).then((data) => {
  console.log('s',data) // 同时默认在上层返回undefined => s undefined
},(err)=>{
  console.log('f',err)
})
1
2
3
4
5
6
7
8
9

上面的这个例子中,虽然在第一个then中抛出异常,但是没有显示的返回数据,其实就是返回undefined, 会作为普通值传递给下一个then。

# 情况3:then中方法返回的是一个promsie对象

此时会根据promsie的结果来处理是走成功还是失败。


 








readFile("./a.txt", "utf8").then((value)=>{
  return readFile('b.txt') // 这个执行的结果是 b
},(err) => {
  console.log('fail',err) 
}).then((data) => {
  console.log('s',data)  // => s b
},(err)=>{
  console.log('f',err)
})
1
2
3
4
5
6
7
8
9

在第{2}行代码中, readFile('b.txt') 这个函数的执行结结果是返回一个b,就可以在下一个then的成功回调中拿到这个值。反之,如果readFile('b.txt')返回的是一个失败的结果,那么就会走到第二个then的失败回调。


 








readFile("./a.txt", "utf8").then((value)=>{
  return readFile('b1.txt') // b1.txt 并不存在
},(err) => {
  console.log('fail',err) 
}).then((data) => {
  console.log('s',data)  
},(err)=>{
  console.log('f',err) // => 走到这里
})
1
2
3
4
5
6
7
8
9

# 总结一下:

then方法如果返回一个普通值(除了promsie)就会传递给下一个then的成功。如果返回一个失败的promsie或者抛出异常,会走下一个then的失败。

let promise2 = new Promise((resolve, reject) => {
  resolve('成功')
}).then((data) => {
  console.log(data) // => ‘成功’
  return 'x'
})

promise2.then((data) => {
    console.log("success", data) // => success x
  },(err) => {
    console.log("failed", err)
  }
)
1
2
3
4
5
6
7
8
9
10
11
12
13

从上面代码我们已经知道,每调用then方法一次,都会产生一个新的promsie,并且第一次then的时候,无论是成功和是失败,都会有隐含的返回值,如果不指定就是返回undefined,那这个返回值,是能够成为外面then的入参继续传递下去。

那如果在第一个then方法的成功回调中抛出异常,函数该如何执行呢?

let promise2 = new Promise((resolve, reject) => {
  resolve('成功')
}).then((data) => {
  console.log(data) // => 成功
  throw new Error("抛出异常")
})

promise2.then((data) => {
    console.log("success", data) 
  },(err) => {
    console.log("failed", err) // => failed  errorxxxx
  }
)
1
2
3
4
5
6
7
8
9
10
11
12
13

如果在第一个promise的成功回调函数中, 抛出异常,会在第二个then的方法中的失败回调中得到结果。这很显然是调用了第二个promsie的resolve或者reject方法,根据此,我们需要同步捕获错误异常

# 第二版代码









































 








































 
 





// promsie 有三种状态,分别是 等待  成功  失败 我们用常量来表示。
const PENDING = "PENDING"
const FULFILLED = "FULFILLED"
const REJECTED = "REJECTED"
// Promsie 内部是一个类,我们这里使用 class 来实现
class Promise {
  // 构造函数的入参就是自执行函数 executor
  constructor(executor) {
    this.status = PENDING // promise默认的状态
    this.value = undefined // 成功的原因
    this.reason = undefined // 失败的原因
    this.onResolvedCallbacks = []; // 存放成功的回调方法
    this.onRejectedCallbacks = []; // 存放失败的回调方法
    // 成功resolve函数
    const resolve = (value) => {
      // 只有在 PENDING 状态下 才能 修改状态
      if (this.status === PENDING) {
        this.value = value
        this.status = FULFILLED // 修改状态
        this.onResolveCallbacks.forEach((fn) => fn())
      }
    }
    // 失败的reject函数
    const reject = (reason) => {
      if (this.status === PENDING) {
        this.reason = reason
        this.status = REJECTED // 修改状态
        this.onRejecteCallbacks.forEach((fn) => fn())
      }
    }
    try {
      executor(resolve, reject)
    } catch (e) {
      // 立即执行函数执行的时候如果抛出异常,直接走reject函数。
      reject(e)
    }
  }
  then(onFulfilled, onRejected) {
    // 每次then的时候 都会产生一个新的promsie,我们将这个新的promsie
    // 命名为Promsie2。又因为promise接收的是一个立即执行函数,并不会影响结果。
    let promsie2 = new Promise((resolve, reject) => {
      if(this.status == PENDING) {
        this.onResolvedCallbacks.push(() => { 
          // 这里就是用来同步捕获异常场景的
          try {
            // todo...
            let x = onFulfilled(this.value);
            resolve(x)
          catch(e) {
            reject(e)
          }
        });
        this.onRejectedCallbacks.push(() => {
          try {
            // todo...
            let x = onRejected(this.reason);
            resolve(x)
          catch(e) {
            reject(e)
          }
        });
      }
      // onFulfilled, onRejected
      if (this.status == FULFILLED) {
        try {
          // 成功调用成功方法,并传入成功的值
          let x = onFulfilled(this.value)
          resolve(x)
        } catch(e) {
          reject(e)
        }
      }
      if (this.status === REJECTED) {
        try {
          // 失败调用失败方法  并传入失败的原因
          let x = onRejected(this.reason)
          resolve(x)
        } catch(e) {
          reject(e)
        }
      }
    })
    return promsie2
  }
}

module.exports = Promise
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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87

在promsieA+ 规范中有说过这样的点, onFulfilled 和 onRejected 不能在当前上下文中调用,这里我就必须想办法,让这两个函数异步触发,异步触发,这里我们使用setTimeout来实现,原因是我们没有办法模拟浏览器的微任务处理逻辑。

# 第三版代码












































 
 
 
 
 
 
 
 
 


 
 
 
 
 
 
 
 
 
































// promsie 有三种状态,分别是 等待  成功  失败 我们用常量来表示。
const PENDING = "PENDING"
const FULFILLED = "FULFILLED"
const REJECTED = "REJECTED"
// Promsie 内部是一个类,我们这里使用 class 来实现
class Promise {
  // 构造函数的入参就是自执行函数 executor
  constructor(executor) {
    this.status = PENDING // promise默认的状态
    this.value = undefined // 成功的原因
    this.reason = undefined // 失败的原因
    this.onResolvedCallbacks = []; // 存放成功的回调方法
    this.onRejectedCallbacks = []; // 存放失败的回调方法
    // 成功resolve函数
    const resolve = (value) => {
      // 只有在 PENDING 状态下 才能 修改状态
      if (this.status === PENDING) {
        this.value = value
        this.status = FULFILLED // 修改状态
        this.onResolveCallbacks.forEach((fn) => fn())
      }
    }
    // 失败的reject函数
    const reject = (reason) => {
      if (this.status === PENDING) {
        this.reason = reason
        this.status = REJECTED // 修改状态
        this.onRejecteCallbacks.forEach((fn) => fn())
      }
    }
    try {
      executor(resolve, reject)
    } catch (e) {
      // 立即执行函数执行的时候如果抛出异常,直接走reject函数。
      reject(e)
    }
  }
  then(onFulfilled, onRejected) {
    // 每次then的时候 都会产生一个新的promsie,我们将这个新的promsie
    // 命名为Promsie2。又因为promise接收的是一个立即执行函数,并不会影响结果。
    let promsie2 = new Promise((resolve, reject) => {
      if(this.status == PENDING) {
        this.onResolvedCallbacks.push(() => { 
          setTimeout(() => {
            try {
              // todo...
              let x = onFulfilled(this.value);
              resolve(x)
            } catch(e) {
              reject(e)
            }
          },0)
        });
        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              // todo...
              let x = onRejected(this.reason);
              resolve(x)
            } catch(e) {
              reject(e)
            }
          },0)
        });
      }
      // onFulfilled, onRejected
      if (this.status == FULFILLED) {
        setTimeout(() => {
          try {
            // 成功调用成功方法,并传入成功的值
            let x = onFulfilled(this.value)
            resolve(x)
          } catch(e) {
            reject(e)
          }
        },0)
      }
      if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            // 失败调用失败方法  并传入失败的原因
            let x = onRejected(this.reason)
            resolve(x)
          } catch(e) {
            reject(e)
          }
        },0)
      }
    })
    return promsie2
  }
}

module.exports = Promise
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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94

在第一个promise then 方法中很可能返回的还是一个promsie, 也就是说 onFulfilled 执行完的x可能是一个Promise。类似于下面的代码:

let promise2 = new Promise((resolve) => {
  resolve(1)
}).then((data) => {
    // onFulfilled 函数的返回值x 可能还是个 Promsie
    return new Promise((resolve, reject) => {
      // x 可能是一个promise
      setTimeout(() => {
        resolve("ok")
      }, 1000)
    })
  },(err) => {
    return 111
  }
)
promise2.then((data) => {
    console.log(data) // ok 
  },(err) => {
    console.log("error", err)
  }
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

我们可以看到,第二个then的成功回调中拿到的是新的创建出来的promise的结果。根据上面的描述,我们需要专门创建一个处理函数来针对x的不同取值做相应的操作,这个方法是相对独立的。

# 第四版代码
















































 










 












 










 












// promsie 有三种状态,分别是 等待  成功  失败 我们用常量来表示。
const PENDING = "PENDING"
const FULFILLED = "FULFILLED"
const REJECTED = "REJECTED"
// Promsie 内部是一个类,我们这里使用 class 来实现
class Promise {
  // 构造函数的入参就是自执行函数 executor
  constructor(executor) {
    this.status = PENDING // promise默认的状态
    this.value = undefined // 成功的原因
    this.reason = undefined // 失败的原因
    this.onResolvedCallbacks = []; // 存放成功的回调方法
    this.onRejectedCallbacks = []; // 存放失败的回调方法
    // 成功resolve函数
    const resolve = (value) => {
      // 只有在 PENDING 状态下 才能 修改状态
      if (this.status === PENDING) {
        this.value = value
        this.status = FULFILLED // 修改状态
        this.onResolveCallbacks.forEach((fn) => fn())
      }
    }
    // 失败的reject函数
    const reject = (reason) => {
      if (this.status === PENDING) {
        this.reason = reason
        this.status = REJECTED // 修改状态
        this.onRejecteCallbacks.forEach((fn) => fn())
      }
    }
    try {
      executor(resolve, reject)
    } catch (e) {
      // 立即执行函数执行的时候如果抛出异常,直接走reject函数。
      reject(e)
    }
  }
  then(onFulfilled, onRejected) {
    // 每次then的时候 都会产生一个新的promsie,我们将这个新的promsie
    // 命名为Promsie2。又因为promise接收的是一个立即执行函数,并不会影响结果。
    let promsie2 = new Promise((resolve, reject) => {
      if(this.status == PENDING) {
        this.onResolvedCallbacks.push(() => { 
          setTimeout(() => {
            try {
              // todo...
              let x = onFulfilled(this.value);
              resolvePromise(promsie2, x, resolve, reject)
            } catch(e) {
              reject(e)
            }
          },0)
        });
        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              // todo...
              let x = onRejected(this.reason);
              resolvePromise(promsie2, x, resolve, reject)
            } catch(e) {
              reject(e)
            }
          },0)
        });
      }
      // onFulfilled, onRejected
      if (this.status == FULFILLED) {
        setTimeout(() => {
          try {
            // 成功调用成功方法,并传入成功的值
            let x = onFulfilled(this.value)
            resolvePromise(promsie2, x, resolve, reject)
          } catch(e) {
            reject(e)
          }
        },0)
      }
      if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            // 失败调用失败方法  并传入失败的原因
            let x = onRejected(this.reason)
            resolvePromise(promsie2, x, resolve, reject)
          } catch(e) {
            reject(e)
          }
        },0)
      }
    })
    return promsie2
  }
}

module.exports = Promise
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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94

# resolvePromise函数处理。

应该遵循以下原则:

  • 1 如果promsie2和x是同一个promsie,应该抛出异常。


 
 
 


function resolvePromise(promsie2, x, resolve, reject) {
  // 核心流程
  if (promsie2 === x) {
    return reject(new TypeError("错误"))
  }
}
1
2
3
4
5
6
  • 2 我们还需要考虑兼容别人的Promise 这部分逻辑稍微复杂一些。首先promsie是一个对象或者函数,如果是普通值直接返回就好,还需要明确Promise中是必须拥有then方法的,在获取then方法的时候可能会抛出异常,因此这部分需要做异常捕获操作。






 
 
 
 
 
 
 
 
 
 
 


function resolvePromise(promsie2, x, resolve, reject) {
  // 核心流程
  if (promsie2 === x) {
    return reject(new TypeError("错误"))
  }
  // 我可能写的promise 要和别人的promise兼容,考虑不是自己写的promise情况
  if ((typeof x === "object" && x !== null) || typeof x === "function") {
    // 有可能是promise 还必须拥有then方法,在取 then的时候,可能会抛出异常。
    // 这里应该捕获异常
    try {
      let then = x.then
    }catch(e) {
      reject(e) // 如果捕获到了异常,将异常直接传递给 reject 函数
    }
  } else {
    resolve(x) // 说明返回的是一个普通值 直接将他放到 promise2.resolve中。
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  • 3 接下来,我们还需要判断 then 是不是一个函数,如果then 是一个函数,那么就要绑定this指向。如果不是一个函数,就是一种普通的值的处理逻辑。
  • 4 别人的promsie可能会有致命的错误,就是调取完resolve之后,还可能调取reject逻辑。所以需要设置一个锁来保证调用的结果正确。











 
 
 
 
 
 
 
 
 
 
 
 
 













function resolvePromise(promsie2, x, resolve, reject) {
  // 核心流程
  if (promsie2 === x) {
    return reject(new TypeError("错误"))
  }
  // 我可能写的promise 要和别人的promise兼容,考虑不是自己写的promise情况
  if ((typeof x === "object" && x !== null) || typeof x === "function") {
    // 有可能是promise 还必须拥有then方法,在取 then的时候,可能会抛出异常。
    let called = false
    try {
      let then = x.then
      if (typeof then === 'function') {
        // 这里就认为是Promise了 这里面不 用 x.then的愿因是 可能还会触发自定义的getter
        // 如果成功就将成功的结果给promise2的resolve方法,如果失败, 就将失败的原因传递给reject方法
        // 这里只是稍微改变了 then的this指向。
        then.call(x, (y) => {
          if (called) return 
          called = true
          resolve(y)
        },(r) => {
          if (called) return 
          called = true
          reject(r)
        })
      } else { // then 是一个对象 直接使用resolve 函数返回出去
        resolve(x)
      }
    }catch(e) {
      if (called) return 
      called = true
      reject(e) // 如果捕获到了异常,将异常直接传递给 reject 函数
    }
  } else {
    resolve(x) // 说明返回的是一个普通值 直接将他放到 promise2.resolve中。
  }
}
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
  • 5 还有一种嵌套调用promise的情况,这种情况需要做一个递归调用。


















 


















function resolvePromise(promsie2, x, resolve, reject) {
  // 核心流程
  if (promsie2 === x) {
    return reject(new TypeError("错误"))
  }
  // 我可能写的promise 要和别人的promise兼容,考虑不是自己写的promise情况
  if ((typeof x === "object" && x !== null) || typeof x === "function") {
    // 有可能是promise 还必须拥有then方法,在取 then的时候,可能会抛出异常。
    let called = false
    try {
      let then = x.then
      if (typeof then === 'function') {
        // 这里就认为是Promise了 这里面不 用 x.then的愿因是 可能还会触发自定义的getter
        // 如果成功就将成功的结果给promise2的resolve方法,如果失败, 就将失败的原因传递给reject方法
        // 这里只是稍微改变了 then的this指向。
        then.call(x, (y) => {
          if (called) return 
          called = true
          resolvePromise(promsie2, y, resolve, reject)
        },(r) => {
          if (called) return 
          called = true
          reject(r)
        })
      } else { // then 是一个对象 直接使用resolve 函数返回出去
        resolve(x)
      }
    } catch(e) {
      if (called) return 
      called = true
      reject(e) // 如果捕获到了异常,将异常直接传递给 reject 函数
    }
  } else {
    resolve(x) // 说明返回的是一个普通值 直接将他放到 promise2.resolve中。
  }
}
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

onFulfilled, onRejected 在使用then的时候很可能是不传递的。此时需要针对这种情况单独做处理。做一层判断。贴出完整实现

# 第五版代码





















































































 
 




























































const PENDING = "PENDING"
const FULFILLED = "FULFILLED"
const REJECTED = "REJECTED"

// 利用x的值来判断是调用promise2的resolve还是reject
function resolvePromise(promise2, x, resolve, reject) {
  // 核心流程
  if (promise2 === x) {
    return reject(new TypeError("错误"))
  }
  // 我可能写的promise 要和别人的promise兼容,考虑不是自己写的promise情况
  if ((typeof x === "object" && x !== null) || typeof x === "function") {
    // 有可能是promise
    // 别人的promise可能调用成功后 还能调用失败~~~  确保了别人promise符合规范
    let called = false
    try {
      // 有可能then方法是通过defineProperty来实现的 取值时可能会发生异常
      let then = x.then
      if (typeof then === "function") {
        // 这里我就认为你是promise了  x.then 这样写会触发getter可能会发生异常
        then.call(
          x,
          (y) => {
            if (called) return
            called = true
            resolvePromise(promise2, y, resolve, reject) // 直到解析他不是promise位置
          },
          (r) => {
            // reason
            if (called) return
            called = true
            reject(r)
          }
        )
      } else {
        // {}  {then:{}}
        resolve(x) // 常量
      }
    } catch (e) {
      if (called) return
      called = true
      reject(e)
    }
  } else {
    resolve(x) // 说明返回的是一个普通值 直接将他放到promise2.resolve中
  }
}

class Promise {
  constructor(executor) {
    this.status = PENDING // promise默认的状态
    this.value = undefined // 成功的原因
    this.reason = undefined // 失败的原因
    this.onResolvedCallbacks = [] // 存放成功的回调方法
    this.onRejectedCallbacks = [] // 存放失败的回调方法
    const resolve = (value) => {
      // 成功resolve函数
      if (value instanceof Promise) {
        return value.then(resolve, reject)
      }
      if (this.status === PENDING) {
        this.value = value
        this.status = FULFILLED // 修改状态
        // 发布
        this.onResolvedCallbacks.forEach((fn) => fn())
      }
    }
    const reject = (reason) => {
      // 失败的reject函数
      if (this.status === PENDING) {
        this.reason = reason
        this.status = REJECTED // 修改状态
        this.onRejectedCallbacks.forEach((fn) => fn())
      }
    }
    try {
      executor(resolve, reject)
    } catch (e) {
      reject(e)
    }
  }
  // then中的参数是可选的
  then(onFulfilled, onRejected) {
    // onFulfilled, onRejected
    onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (v) => v
    onRejected = typeof onRejected === "function" ? onRejected : (err) => { throw err } 
    // 用于实现链式调用
    let promise2 = new Promise((resolve, reject) => {
      // 订阅模式
      if (this.status == FULFILLED) {
        // 成功调用成功方法
        setTimeout(() => {
          try {
            let x = onFulfilled(this.value)
            // 此x 可能是一个promise, 如果是promise需要看一下这个promise是成功还是失败 .then ,如果成功则把成功的结果 调用promise2的resolve传递进去,如果失败则同理

            // 总结 x的值 决定是调用promise2的 resolve还是reject,如果是promise则取他的状态,如果是普通值则直接调用resolve
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        }, 0)
      }
      if (this.status === REJECTED) {
        // 失败调用失败方法
        setTimeout(() => {
          try {
            let x = onRejected(this.reason)
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        }, 0)
      }
      if (this.status == PENDING) {
        // 代码是异步调用resolve或者reject的
        this.onResolvedCallbacks.push(() => {
          // 切片编程 AOP
          setTimeout(() => {
            try {
              // todo...
              let x = onFulfilled(this.value)
              resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          }, 0)
        })
        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              // todo...
              let x = onRejected(this.reason)
              resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          }, 0)
        })
      }
    })
    return promise2
  }
}
module.exports = Promise
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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145

至此,一个手写版本的Promise就实现了。

最后更新时间: 3/31/2022, 7:35:08 PM