前端修仙之路

ES6-promise学习及手写promise

2019-10-09  本文已影响0人  月上秦少
image

1. promise要解决的问题:

脑筋急转弯:把牛关进冰箱里,要分几步?

// 第一步,打开冰箱
function open(){
    setTimeout(()=>{
        console.log('打开冰箱');
        return 'success';
    }, 1000)
}

// 第二步,放牛进去
function settle(){
      setTimeout(()=>{
       console.log('放牛进去');
       return 'success';
    }, 3000)
}

// 第三步,关上冰箱
function close(){
      setTimeout(()=>{
       console.log('关上冰箱');
       return 'success';
    }, 1000)
}

function closeCow(){
    open();
    settle();
    close()
}

closeCow();

//"打开冰箱"
//"关上冰箱"?
//"放牛进去"?

很显然,这三个操作不能颠倒顺序,否则任务就会失败。但是上述逻辑并不能保证最终是按照我们想要的顺序进行,显然,难点在于第二步,花费的时间更长。为了保证第二步在第一步执行成功之后再执行,第三步在第二步执行成功之后在执行,改进:

function closeCow() {
    setTimeout(() => {
        console.log("打开冰箱");
        setTimeout(() => {
            console.log("放牛进去");
            setTimeout(() => {
                console.log("关闭冰箱");
            }, 1000);
        }, 3000);
    }, 1000);
}

这样的确解决了问题,但是看起来很别扭,逻辑不清晰,这就是经典的“回调地狱”。在过去,要想做多重的异步操作,会导致经典的回调地狱,promise的出现,就是为了解决这个问题的。

Promise对象用于表示一个异步操作的最终完成 (或失败), 及其结果值。

let closeCow = new Promise((resolve, reject) => {
    console.log('把牛放进冰箱');
    resolve();
});
closeCow
    .then(open())   // 打开冰箱
    .then(settle()) // 放牛进去
    .then(close()); // 关上冰箱

这就是promise最简单的应用。

2. promise深入了解

2.1 Promise状态

2.2 promise实例方法

所谓实例方法(instance method)就是必须实例化之后才能调用的方法,在JS中表现为使用new关键字实例化之后才能调用的,是定义在原型上的方法,即Promise.prototype.methodName;

2.3 promise静态方法

所谓静态方法(static method)就是可以直接通过对象调用,而不用实例化的方法,即Promise.methodName();

2.3 promise属性

3. JS事件执行机制(Event Loop)

为了更好地理解promise的应用,先需要理解JS执行机制。

运行时概念

一个 JavaScript 运行时包含了一个待处理的消息队列。每一个消息都关联着一个用以处理这个消息的函数。

函数的处理会一直进行到执行栈再次为空为止;然后事件循环将会处理队列中的下一个消息(如果还有的话)。

事件循环机制

[图片上传失败...(image-d11d0d-1570631381694)]

JS执行时,将任务分为同步任务异步任务,同步任务都在主线程上执行(主代码块),形成一个执行栈,异步任务会被加入到任务队列里面。

任务队列中的任务分为两种任务类型:macrotask和microtask任务队列里面微任务优先于宏任务执行,先执行完任务队列里面所有的微任务,然后再执行任务队列里面的宏任务。

JS运行机制:

参考:

4. promise测试题

4.1 示例一:

new Promise(resolve => {
// promise构造函数里面是同步代码区,和普通代码块一样
    console.log(5);
    resolve(1);
    
    Promise.resolve().then(() => {
        console.log(2)
    });
    console.log(4)
}).then(t => {
    console.log(t)
}).catch(()=>{
    console.log(6);
}).finally(() =>{
    console.log(0);
});
console.log(3);

<details>
<summary>输出结果</summary>
<pre>5 4 3 2 1 0</pre>
</details>

4.2 示例二:

console.log('script start'); // 宏任务1

setTimeout(function() {
  console.log('setTimeout'); // 宏任务2
}, 0);

Promise.resolve().then(function() { 
  console.log('promise1');// 微任务1
}).then(function() {
  console.log('promise2'); // 微任务1
});

console.log('script end'); // 宏任务1

<details>
<summary>输出结果</summary>
<pre>
script start
script end
promise1
promise2
setTimeout
</pre>
</details>

4.3 示例三:

let p1 = new Promise((resolve,reject)=>{
  let num = 6
  if(num<5){
    console.log('resolve1')
    resolve(num)
  }else{
    console.log('reject1')
    reject(num)
  }
})
p1.then((res)=>{
  console.log('resolve2')
  console.log(res)
},(rej)=>{
  console.log('reject2')
  let p2 = new Promise((resolve,reject)=>{
    if(rej*2>10){
      console.log('resolve3')
      resolve(rej*2)
    }else{
      console.log('reject3')
      reject(rej*2)
    }
  })
  return p2
}).then((res)=>{
  console.log('resolve4')
  console.log(res)
},(rej)=>{
  console.log('reject4')
  console.log(rej)
})

<details>
<summary>输出结果</summary>
<pre>
reject1
reject2
resolve3
resolve4
12
</pre>
</details>

4.4 示例四:

Tasks, microtasks, queues and schedules

<iframe height="302" style="width: 100%;" scrolling="no" title="ES6-promise-demo3" src="https://codepen.io/keekuun/embed/abbzjbM?height=302&theme-id=0&default-tab=js,result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href='https://codepen.io/keekuun/pen/abbzjbM'>ES6-promise-demo3</a> by Keekuun
(<a href='https://codepen.io/keekuun'>@keekuun</a>) on <a href='https://codepen.io'>CodePen</a>.
</iframe>

<details>
<summary>点击内部输出</summary>
<pre>
click
promise
mutate
click
promise
mutate
timeout
timeout
</pre>
</details>

4.5 示例五:

从event loop到async await来了解事件循环机制

async function a1 () { // async关键字
    console.log('a1 start')
    await a2() // await关键字
    console.log('a1 end')
}
async function a2 () {
    console.log('a2')
}

console.log('script start')

setTimeout(() => {
    console.log('setTimeout')
}, 0)

Promise.resolve().then(() => {
    console.log('promise1')
})

a1()

let promise2 = new Promise((resolve) => {
    resolve('promise2.then')
    console.log('promise2')
})

promise2.then((res) => {
    console.log(res)
    Promise.resolve().then(() => {
        console.log('promise3')
    })
})
console.log('script end')

<details>
<summary>输出结果</summary>
<pre>
script start
a1 start
a2
promise2
script end
promise1
a1 end
promise2.then
promise3
setTimeout
</pre>
</details>

4.6 示例五:

async function test() { 
    console.log('test start'); 
    await undefined; 
    console.log('await 1'); 
    await new Promise(resolve => {  
        console.log('promise in async'); 
        resolve(); 
    }); 
    console.log('await 2'); 
} 
 
test(); 
new Promise((resolve) => { 
    console.log('promise'); 
    resolve(); 
}) 
.then(() => {console.log(1)}) 
.then(() => {console.log(2)}) 
.then(() => {console.log(3)}) 
.then(() => {console.log(4)});

<details>
<summary>输出结果</summary>
<pre>
test start
promise
await 1
promise in async
1
await 2
2
3
4
</pre>
</details>

5. 手写promise

(function () {
    // 判断function
    function isFunction(fn) {
        return typeof fn === 'function';
    }

    // 状态 pending、fulfilled、rejected
    var PENDING = 'pending';
    var FULFILLED = 'fulfilled';
    var REJECTED = 'rejected';

    // 构造方法
    var Kromise = function (handle) {
        // 当前状态
        this._status = PENDING;
        // 添加成功回调队列
        this._fulfilledQueue = [];
        // 添加失败回调队列
        this._rejectedQueue = [];
        // 引用当前this对象
        var self = this;

        if (!isFunction(handle)) {
            throw new Error('Parameter handle is not a function!')
        }

        // 添加resolve时执行的函数
        function _resolve(val) {
            var run = function () {
                if (self._status !== PENDING) return;
                // 依次执行成功队列中的函数,并清空队列
                var runFulfilled = function (res) {
                    var resolve;
                    while (resolve = self._fulfilledQueue.shift()) { // 出栈
                        resolve(res);
                    }
                };

                // 依次执行失败队列中的函数,并清空队列
                var runRejected = function (err) {
                    var reject;
                    while (reject = self._rejectedQueue.shift()) { // 出栈
                        reject(err);
                    }
                };
                /* 如果resolve的参数为Kromise对象,则必须等待该Kromise对象状态改变后,
                 * 当前Kromise的状态才会改变,且状态取决于参数Kromise对象的状态
                 */
                if (val instanceof Kromise) {
                    val.then(function (value) {
                        self._status = FULFILLED;
                        self._value = value;
                        runFulfilled(value)
                    }, function (err) {
                        self._status = REJECTED;
                        self._value = err;
                        runRejected(err);
                    })
                } else {
                    self._status = FULFILLED;
                    self._value = val;
                    runFulfilled(val);
                }

            };
            // 为了支持同步的Promise,这里采用异步调用
            setTimeout(run, 0)
        }

        // 添加reject时执行的函数
        function _reject(err) {
            var run = function () {
                if (self._status !== PENDING) return;
                // 依次执行成功队列中的函数,并清空队列
                self._status = REJECTED;
                self._value = err;
                var reject;
                while (reject = self._fulfilledQueue.shift()) { // 出栈
                    reject(err);
                }
            };
            // 为了支持同步的Promise,这里采用异步调用
            setTimeout(run, 0)
        }

        // 执行handle,捕获异常
        try {
            handle(_resolve.bind(this), _reject.bind(this));
        } catch (e) {
            _reject(e);
        }
    };

    // 属性
    Kromise.length = 1;

    // 实例方法
    // 实现then方法
    Kromise.prototype.then = function (onFulfilled, onRejected) {
        var self = this;
        // 返回一个新的Kromise对象
        return new Kromise(function (onFulfilledNext, onRejectedNext) {
            // 成功时的回调
            var fulfilled = function (val) {
                try {
                    // 如果不是函数,值穿透
                    if (!isFunction(onFulfilled)) {
                        onFulfilledNext(val)
                    } else {
                        var res = onFulfilled(val);
                        // 如果当前回调函数返回Kromise对象,必须等待其状态改变后在执行下一个回调
                        if (res instanceof Kromise) {
                            res.then(onFulfilledNext, onRejectedNext);
                        } else {
                            //否则会将返回结果直接作为参数,传入下一个then的回调函数,并立即执行下一个then的回调函数
                            onFulfilledNext(res);
                        }
                    }
                } catch (e) {
                    // 如果函数执行出错,新的Kromise对象的状态为失败
                    onRejectedNext(e);
                }
            };
            // 失败时的回调
            var rejected = function (err) {
                try {
                    if (!isFunction(onRejected)) {
                        onRejectedNext(err)
                    } else {
                        var res = onRejected(err);
                        if (res instanceof Kromise) {
                            res.then(onFulfilledNext, onRejectedNext);
                        } else {
                            onFulfilledNext(res);
                        }
                    }
                } catch (e) {
                    onRejectedNext(e)
                }
            };

            switch (self._status) {
                // 当状态为pending时,将then方法回调函数加入执行队列等待执行
                case PENDING:
                    self._fulfilledQueue.push(fulfilled);
                    self._rejectedQueue.push(rejected);
                    break;
                // 当状态已经改变时,立即执行对应的回调函数
                case FULFILLED:
                    fulfilled(self._value);
                    break;
                case REJECTED:
                    rejected(self._value);
                    break;
            }
        });
    };

    // 实现catch方法
    Kromise.prototype.catch = function (onRejected) {
        return this.then(undefined, onRejected);
    };

    // 实现finally方法
    Kromise.prototype.finally = function (onFinally) {
        return this.then(function (value) {
            Kromise.resolve(onFinally()).then(function () {
                return value;
            })
        }, function (err) {
            Kromise.resolve(onFinally()).then(function () {
                throw new Error(err);
            })
        })
    };

    // 静态方法
    // 实现resolve方法
    Kromise.resolve = function (value) {
        // 如果参数是Kromise实例,直接返回这个实例
        if (value instanceof Kromise) {
            return value;
        }
        return new Kromise(function (resolve) {
            resolve(value)
        })
    };
    // 实现reject方法
    Kromise.reject = function (value) {
        return new Kromise(function (resolve, reject) {
            reject(value)
        })
    };
    // 实现all方法
    Kromise.all = function (arr) {
        var self = this;
        return new Kromise(function (resolve, reject) {
            var values = [];
            for (var i = 0, len = arr.length; i < len; i++) {
                // 数组参数如果不是Kromise实例,先调用Kromise.resolve
                self.resolve(arr[i]).then(function (res) {
                    values.push(res);
                    // 所有状态都变成fulfilled时返回的Kromise状态就变成fulfilled
                    if (values.length === arr.length) {
                        resolve(values);
                    }
                }, function (e) {
                    // 有一个被rejected时返回的Kromise状态就变成rejected
                    reject(e);
                })
            }
        })
    };

    // 实现race方法
    Kromise.race = function (arr) {
        var self = this;
        return new Kromise(function (resolve, reject) {
            for (var i = 0, len = arr.length; i < len; i++) {
                // 只要有一个实例率先改变状态,新的Kromise的状态就跟着改变
                self.resolve(arr[i]).then(function (res) {
                    resolve(res);
                }, function (err) {
                    reject(err);
                })
            }
        })
    };
    // 实现any方法
    Kromise.any = function (arr) {
        var self = this;
        return new Kromise(function (resolve, reject) {
            var count = 0;
            var errors = [];
            for (var i = 0, len = arr.length; i < len; i++) {
                // 只要有一个实例状态变为fulfilled,新的Kromise状态就会改变为fulfilled
                self.resolve(arr[i]).then(function (res) {
                    resolve(res);
                }, function (err) {
                    errors[count] = err;
                    count++;
                    // 否则等待所有的rejected,新的Kromise状态才会改变为rejected
                    if (count === arr.length) {
                        reject(errors);
                    }
                })
            }
        })

    };
    // 实现allSettled方法
    Kromise.allSettled = function (arr) {
        var results = [];
        var len = arr.length;
        for (var i = 0; i < len; i++) {
            this.resolve(arr[i]).then(function (res) {
                results.push({status: FULFILLED, value: res});
            }, function (err) {
                results.push({status: REJECTED, value: err});
            })
        }
        // 一旦结束,状态总是`fulfilled`,不会变成`rejected`
        return new Kromise(function (resolve, reject) {
            resolve(results)
        })
    };
    // 实现try方法
    Kromise.try = function (fn) {
        if (!isFunction(fn)) return;
        return new Kromise(function (resolve, reject) {
            return resolve(fn());
        })
    };

    // 挂载
    window.Kromise = Kromise;
})();

  // 判断变量否为function
  const isFunction = variable => typeof variable === 'function'
  // 定义Promise的三种状态常量
  const PENDING = 'PENDING'
  const FULFILLED = 'FULFILLED'
  const REJECTED = 'REJECTED'

  class MyPromise {
    constructor (handle) {
      if (!isFunction(handle)) {
        throw new Error('MyPromise must accept a function as a parameter')
      }
      // 添加状态
      this._status = PENDING
      // 添加状态
      this._value = undefined
      // 添加成功回调函数队列
      this._fulfilledQueues = []
      // 添加失败回调函数队列
      this._rejectedQueues = []
      // 执行handle
      try {
        handle(this._resolve.bind(this), this._reject.bind(this)) 
      } catch (err) {
        this._reject(err)
      }
    }
    // 添加resovle时执行的函数
    _resolve (val) {
      const run = () => {
        if (this._status !== PENDING) return
        // 依次执行成功队列中的函数,并清空队列
        const runFulfilled = (value) => {
          let cb;
          while (cb = this._fulfilledQueues.shift()) {
            cb(value)
          }
        }
        // 依次执行失败队列中的函数,并清空队列
        const runRejected = (error) => {
          let cb;
          while (cb = this._rejectedQueues.shift()) {
            cb(error)
          }
        }
        /* 如果resolve的参数为Promise对象,则必须等待该Promise对象状态改变后,
          当前Promsie的状态才会改变,且状态取决于参数Promsie对象的状态
        */
        if (val instanceof MyPromise) {
          val.then(value => {
            this._value = value
            this._status = FULFILLED
            runFulfilled(value)
          }, err => {
            this._value = err
            this._status = REJECTED
            runRejected(err)
          })
        } else {
          this._value = val
          this._status = FULFILLED
          runFulfilled(val)
        }
      }
      // 为了支持同步的Promise,这里采用异步调用
      setTimeout(run, 0)
    }
    // 添加reject时执行的函数
    _reject (err) { 
      if (this._status !== PENDING) return
      // 依次执行失败队列中的函数,并清空队列
      const run = () => {
        this._status = REJECTED
        this._value = err
        let cb;
        while (cb = this._rejectedQueues.shift()) {
          cb(err)
        }
      }
      // 为了支持同步的Promise,这里采用异步调用
      setTimeout(run, 0)
    }
    // 添加then方法
    then (onFulfilled, onRejected) {
      const { _value, _status } = this
      // 返回一个新的Promise对象
      return new MyPromise((onFulfilledNext, onRejectedNext) => {
        // 封装一个成功时执行的函数
        let fulfilled = value => {
          try {
            if (!isFunction(onFulfilled)) {
              onFulfilledNext(value)
            } else {
              let res =  onFulfilled(value);
              if (res instanceof MyPromise) {
                // 如果当前回调函数返回MyPromise对象,必须等待其状态改变后在执行下一个回调
                res.then(onFulfilledNext, onRejectedNext)
              } else {
                //否则会将返回结果直接作为参数,传入下一个then的回调函数,并立即执行下一个then的回调函数
                onFulfilledNext(res)
              }
            }
          } catch (err) {
            // 如果函数执行出错,新的Promise对象的状态为失败
            onRejectedNext(err)
          }
        }
        // 封装一个失败时执行的函数
        let rejected = error => {
          try {
            if (!isFunction(onRejected)) {
              onRejectedNext(error)
            } else {
                let res = onRejected(error);
                if (res instanceof MyPromise) {
                  // 如果当前回调函数返回MyPromise对象,必须等待其状态改变后在执行下一个回调
                  res.then(onFulfilledNext, onRejectedNext)
                } else {
                  //否则会将返回结果直接作为参数,传入下一个then的回调函数,并立即执行下一个then的回调函数
                  onFulfilledNext(res)
                }
            }
          } catch (err) {
            // 如果函数执行出错,新的Promise对象的状态为失败
            onRejectedNext(err)
          }
        }
        switch (_status) {
          // 当状态为pending时,将then方法回调函数加入执行队列等待执行
          case PENDING:
            this._fulfilledQueues.push(fulfilled)
            this._rejectedQueues.push(rejected)
            break
          // 当状态已经改变时,立即执行对应的回调函数
          case FULFILLED:
            fulfilled(_value)
            break
          case REJECTED:
            rejected(_value)
            break
        }
      })
    }
    // 添加catch方法
    catch (onRejected) {
      return this.then(undefined, onRejected)
    }
    // 添加静态resolve方法
    static resolve (value) {
      // 如果参数是MyPromise实例,直接返回这个实例
      if (value instanceof MyPromise) return value
      return new MyPromise(resolve => resolve(value))
    }
    // 添加静态reject方法
    static reject (value) {
      return new MyPromise((resolve ,reject) => reject(value))
    }
    // 添加静态all方法
    static all (list) {
      return new MyPromise((resolve, reject) => {
        /**
         * 返回值的集合
         */
        let values = []
        let count = 0
        for (let [i, p] of list.entries()) {
          // 数组参数如果不是MyPromise实例,先调用MyPromise.resolve
          this.resolve(p).then(res => {
            values[i] = res
            count++
            // 所有状态都变成fulfilled时返回的MyPromise状态就变成fulfilled
            if (count === list.length) resolve(values)
          }, err => {
            // 有一个被rejected时返回的MyPromise状态就变成rejected
            reject(err)
          })
        }
      })
    }
    // 添加静态race方法
    static race (list) {
      return new MyPromise((resolve, reject) => {
        for (let p of list) {
          // 只要有一个实例率先改变状态,新的MyPromise的状态就跟着改变
          this.resolve(p).then(res => {
            resolve(res)
          }, err => {
            reject(err)
          })
        }
      })
    }
    finally (cb) {
      return this.then(
        value  => MyPromise.resolve(cb()).then(() => value),
        reason => MyPromise.resolve(cb()).then(() => { throw reason })
      );
    }
  }
上一篇下一篇

猜你喜欢

热点阅读