关于javascript:JS深挖手撕Promise详细代码完整攻略

46次阅读

共计 4617 个字符,预计需要花费 12 分钟才能阅读完成。

目录:1、new Promise()
2、.then()及链式调用
3、.resolve()和.reject()
4、.all()和.race()


在手撕 Promise 之前咱们得先理解一下 Promise 的个性。
首先 Promise 实质就是一个构造函数,能够通过它发明 Promise 对象实例来实现异步编程的需要。
本篇文章咱们须要实现的 Promise 个性如下:


1、new Promise((resolve,reject) => {})
作为一个构造函数,最先须要实现的是其根本的架构,咱们先实现最根本的性能:

1)status 存储状态、value 和 error 存储胜利 / 失败状态的值
2)resolve 和 reject 办法扭转状态
3)then 办法实现状态扭转后的回调

class MyPromise {constructor(executor) {
    this.status = 'pending';

    this.value;   // 胜利状态的值
    this.error;   // 失败状态的值

    this.resolveCallbacks = [];
    this.rejectedCallbacks = [];

    let resolve = (res) => { }

    let reject = (err) => { }

    executor(resolve, reject)
  }

  then(onFulfilled, onRejected) { }

  catch() {}
}

补充 resolve 和 reject 还有 then 和 catch 的内容

1)then 最根本的性能就是状态扭转后调用回调函数
2)catch 实质就是只有失败状态的.then
class MyPromise {constructor(executor) {
    this.status = 'pending';

    this.value;   // 胜利状态的值
    this.error;   // 失败状态的值

    this.resolveCallbacks = [];
    this.rejectedCallbacks = [];

    let resolve = (res) => {
      // 转变状态、执行回调
      if (this.status === 'pending') {
        this.status = 'resolved';
        this.value = res;
      }
    }

    let reject = (err) => {
      // 转变状态、执行回调
      if (this.status === 'pending') {
        this.status = 'rejected';
        this.error = err;
      }
    }

    executor(resolve, reject)
  }

  then(onFulfilled, onRejected) {
    // 实质就是若以后状态为 resolved 或 rejected 执行对应的回调函数
    if (this.status === 'resolved') {onFulfilled(this.value);
    }

    if (this.status === 'rejected') {onRejected(this.error)
    }
  }

  catch(rejected) {return this.then(null, rejected)
  }
}

这样咱们就能够调用构造函数实现最根本的性能

let pro1 = new MyPromise((resolve, reject) => {resolve('胜利了');
}).then(res => {console.log(res);
})

console.log(pro1);  // 胜利了

2、.then()及链式调用
下面 then 的写法存在几个问题:
1)当状态扭转在异步函数中,此时执行.then 无奈立刻执行回调,应该存储期待状态扭转一起执行
2).then 中的回调应该放在异步函数中,所以须要加 setTimeout
3).then 返回值必须为 Promise 对象,所以每次异步解决的后果设为 x,还需依据 x 决定返回的 promise2 的状态和值

这里引入 resolvePromise 办法决定返回的 promise2 的状态和值。
初步改良如下:

MyPromise.then(onFulfilled, onRejected) {
    // 实质就是若以后状态为 resolved 或 rejected 执行对应的回调函数
    let promise2;

    promise2 = new MyPromise((resolve, reject) => {if (this.status === 'resolved') {setTimeout(() => {let x = onFulfilled(this.value);
          resolvePromise(promise2, x, resolve, reject);
        })
      }

      if (this.status === 'rejected') {setTimeout(() => {let x = onRejected(this.error);
          resolvePromise(promise2, x, resolve, reject);
        })
      }

      if (this.status === 'pending') {this.resolveCallbacks.push(() => {setTimeout(() => {let x = onFulfilled(this.value);
            resolvePromise(promise2, x, resolve, reject);
          })
        });
        this.rejectedCallbacks.push(() => {setTimeout(() => {let x = onRejected(this.error);
            resolvePromise(promise2, x, resolve, reject);
          })
        });
      }
    })

    return promise2;
  }

函数 resolvePromise 的实现是 Promise 实现的外围,其外部蕴含几个重要的规定,简略概括:
1)若返回值 x 是数值,则返回 fulfiiled 状态的 Promise,value 为 x
2)若返回值 x 是 Promise 对象,则持续递归调用
3)只有当返回值 x 为 rejected 状态的 Promise 对象,返回的才是失败状态的 Promise

function resolvePromise(promise2, x, resolve, reject) {
  // 循环援用报错
  if (x === promise2) {
    // reject 报错抛出
    return reject(new TypeError('Chaining cycle detected for promise'));
  }
  // 锁,避免屡次调用
  let called;

  // x 不是 null 且 x 是对象或者函数
  if (x != null && (typeof x === 'object' || typeof x === 'function')) {
    try {
      // A+ 规定,申明 then = x 的 then 办法
      let then = x.then;
      // 如果 then 是函数,就默认是 promise 了
      if (typeof then === 'function') {
        // 就让 then 执行 第一个参数是 this   前面是胜利的回调 和 失败的回调
        then.call(x, y => {
          // 胜利和失败只能调用一个
          if (called) return;
          called = true;
          // resolve 的后果仍旧是 promise 那就持续递归执行
          resolvePromise(promise2, y, resolve, reject);
        }, err => {
          // 胜利和失败只能调用一个
          if (called) return;
          called = true;
          reject(err);// 失败了就失败了
        })
      } else {resolve(x); // 间接胜利即可
      }
    } catch (e) {
      // 也属于失败
      if (called) return;
      called = true;
      // 取 then 出错了那就不要在继续执行了
      reject(e);
    }
  } else {resolve(x);
  }
}

then 实现的最初还需补充一些细节,比方传入的 onFullfilled 若不是函数,则疏忽 onFullfilled 间接返回 value;还有之前的 resolve 和 reject 退出回调数组的调用

onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : error => error;

3、.resolve()和.reject()
Promise 还能够间接调用 resolve 和 reject 办法,其本质就是 new Promise 而后扭转状态。

MyPromise.resolve = (value) => {return new MyPromise((resolve, reject) => {resolve(value);
  })
}

MyPromise.reject = (value) => {return new MyPromise((resolve, reject) => {reject(value);
  })
}

4、.all()和.race()
首先说说 all()办法,其本质就是只有当参数里所有 Promise 对象都 fulfilled 才返回值,其有以下个性:
Promise.all([promise1,promise2,…])
1)仅在数组内所有 promise 对象都 resolve,才 resolve,且返回 Promise 对象,value 是所有 promise 对象胜利的 value
2)如果有一个 Promise 对象 reject,则返回第一个 reject 的 promise 后果
3)参数的 promises 只要求是可迭代对象,如果传递的不是 Promise 对象,则间接返回原值即可。

MyPromise.all = (promises) => {
  // promises 是数组
  if (!Array.isArray(promises)) {throw ('必须是数组')
  }
  let res = []

  return new MyPromise((resolve, reject) => {for (let i = 0; i < promises.length; i++) {if (promises[i].then) {
        // 是 promise 对象则.then
        promises[i].then(r => {res.push(r);
          if (res.length === promises.length) {
            // 全副胜利
            return resolve(res);
          }
        }, error => {return reject(error);
        })
      } else {
        // 不是 promise 对象则间接存 value
        res.push(promises[i]);
        if (res.length === promises.length) {
          // 全副胜利
          return resolve(res);
        }
      }
    }
  })
}

race()办法与 all 相同,是只返回参数所有 Promise 对象最早扭转状态的对象,其个性如下:
Promise.race(promises)
1)当数组中某一个 Promise 的状态变为 fulfilled/rejected,则 race 返回那个 Promise 状态及 value
2)间接把 race 的 resolve 注入到数组每个对象的回调中

MyPromise.race = (promises) => {return new MyPromise((resolve, reject) => {for (let i = 0; i < promises.length; i++) {promises[i].then(resolve, reject);
    }
  })
}

所有 Promise 的手撕就实现了。

正文完
 0