前言

不知道Promise是啥的,请看上一篇《异步流程控制》

看了不少Prmose实现的资料,目前的理解就是Promise也就是回调的一种优雅实现。

现在Javascript的重心都放在了async-await,而async-await是建立在Promise之上的,所以深入一下Promise,还是很有必要的。

实现功能分析

原生Promise

  1. new Promise(function (resolve,reject){
  2. setTimeout(function (){
  3. console.log(1)
  4. resolve(2)
  5. },1000)
  6. }).then(function (val){
  7. setTimeout(function (){
  8. console.log('2:'+val)
  9. },1000)
  10. }).then(function (val){
  11. setTimeout(function (){
  12. console.log('3:'+val)
  13. },1000)
  14. })
  15. // 1
  16. // 2:2
  17. // 3:und

开始实现

浅谈 Promise 实现 - 图1

创建一个_promise函数,由简到繁

简单功能

源码:

  1. function _promise(fn){
  2. //resolve时的回调
  3. var callback;
  4. //一个实例的方法,用来注册异步事件
  5. this.then = function(done){
  6. callback = done;
  7. }
  8. function resolve(val){
  9. callback(val);
  10. }
  11. fn(resolve);
  12. }

使用:

  1. new _promise(function (resolve){
  2. setTimeout(function (){
  3. console.log(1)
  4. resolve(2)
  5. },1000)
  6. }).then(function (val){
  7. setTimeout(function (){
  8. console.log('2:'+val)
  9. },1000)
  10. })
  11. // 1
  12. // 2:2

步骤分析:

  1. 传入fn执行,并调用then方法传入异步执行完毕后的函数赋值给Promise内部的callback
  2. Promise内部fn执行并将resolve函数作为参数传入
  3. fn执行到成功位置,执行resolve函数并传入成功值(val)
  4. resolve函数执行回调callback并传入成功值

链式支持

实现:

在then中返回this,并把then的回调存放到$resolve数组中,执行resolve使用forEach依次调用

  1. function _promise(fn){
  2. // resolve时的回调
  3. var promise = this,
  4. value = null,
  5. promise.$resolve = [] // 存放需要调用的函数
  6. // 返回this,支持链式调用
  7. this.then = function (onFulfilled){
  8. promise.$resolve.push(onFulfilled)
  9. return this
  10. }
  11. // 成功回调,把$resolve存放的函数依次执行
  12. function resolve(value){
  13. promise.$resolve.forEach(function (callback){
  14. callback(value)
  15. })
  16. }
  17. fn(resolve)
  18. }

可是如果then中的传入函数是同步的话,就无法控制回调执行顺序

比如:

  1. new _promise(function (resolve){
  2. for(var i = 0,num; i < 1000;i++){
  3. num=Math.random()*100
  4. }
  5. console.log(1)
  6. resolve(2)
  7. }
  8. }).then(function (val){
  9. setTimeout(function (){
  10. console.log('2:'+val)
  11. },1000)
  12. }).then(function (val){
  13. setTimeout(function (){
  14. console.log('3:'+val)
  15. },1000)
  16. })
  17. // 1

只会执行fn,回调都没有执行,因为这时在resolve比then先执行,这时候promise.$resolve数组是空数组,所以上面代码只打印了一个1。我们需要把resolve放到下一个任务队列末尾执行,也就是then的回调都添加到了数组中之后

修改resolve函数

  1. function resolve(value){
  2. setTimeout(function (){
  3. promise.$resolve.forEach(function (callback){
  4. callback(value)
  5. })
  6. },0)
  7. }

引入状态

分析:

每个 Promise 存在三个互斥状态:pending、fulfilled、rejected。Promise 对象的状态改变,只有两种可能:从 pending 变为 fulfilled 和从 pending 变为 rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对 Promise 对象添加回调函数,也会立即得到这个结果。

源码:

promise.value保存之后调用该对象回调的值

  1. function _promise(fn){
  2. // 成功的回调
  3. var promise = this
  4. promise.value = null
  5. promise.$resolve = [],
  6. promise.$status = 'PENDING'
  7. this.then = function (onFulfilled){
  8. if(promise.$status === 'PENDING'){
  9. promise.$resolve.push(onFulfilled)
  10. return this
  11. }
  12. onFulfilled(promise.value)
  13. return this
  14. }
  15. function resolve(value){
  16. setTimeout(function (){
  17. promise.$status = "FULFILLED";
  18. promise.$resolve.forEach(function (callback){
  19. callback(value)
  20. promise.value = value
  21. })
  22. },0)
  23. }
  24. fn(resolve)
  25. }

使用:

  1. let a = new _promise(function (resolve){
  2. setTimeout(function (){
  3. console.log('1:')
  4. resolve(2)
  5. },1000)
  6. })
  7. a.then(function (val){
  8. console.log('2:'+val)
  9. })
  10. // 1
  11. // 2:2

异步回调返回的结果传递到下一个回调

修改resolve函数

  1. function resolve(value){
  2. setTimeout(function (){
  3. promise.$status = "FULFILLED";
  4. promise.$resolve.forEach(function (callback){
  5. value = callback(value)
  6. promise.value = value
  7. })
  8. },0)
  9. }

即可

失败处理

异步操作不可能都成功,在异步操作失败时,标记其状态为 rejected,并执行注册的失败回调。

有了之前处理 fulfilled 状态的经验,支持错误处理变得很容易。毫无疑问的是,在注册回调、处理状态变更上都要加入新的逻辑:

  1. this.then = function (onFulfilled, onRejected){
  2. return new _promise(function (resolve){
  3. function handle(value){
  4. var ret = typeof onFulfilled === 'function' && onFulfilled(value) || value;
  5. resolve(ret)
  6. }
  7. function errback(reason){
  8. reason = typeof onRejected === 'function' && onRejected(reason) || reason;
  9. reject(reason)
  10. }
  11. if(promise.$status === 'PENDING'){
  12. promise.$resolves.push(handle)
  13. promise.$rejects.push(errback)
  14. } else if (promise.$status === 'FULFILLED') {
  15. handle(promise.$value)
  16. } else if (promise.$status === 'REJECTED') {
  17. errback(promise.$reason)
  18. }
  19. })
  20. }