ES6-promise学习及手写promise

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状态

  • Pending(待处理): promise初始化的状态,正在运行,既没有完成也没有失败的状态,此状态可以提升为fulfilledrejected状态

  • Fulfilled(已完成): 如果回调函数实现Promise的resolve(回调函数),那么state变为fulfilled

  • Rejected(已拒绝): 如果Promise调用过程中遭到拒绝或发生异常,state就会处于rejected状态。

  • Settled(不变的): Promise从pending状态提升后,状态要么变为fulfilled,要么变为rejected,没有其他情况。

    Promise 的状态一旦改变,就永久保持该状态,不会再变了。

2.2 promise实例方法

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

  • Promise.prototype.then(onFulfilled, onRejected): 用来注册当状态变为fulfilled或者reject时的回调函数

    // onFulfilled 是用来接收promise成功的值
    // onRejected 是用来接收promise失败的原因
    promise.then(onFulfilled, onRejected);
    

    注意,then方法是异步执行的,onFulfilledonRejected方法只会执行其中一个,因为promise状态是单向变化的,要么fulfilled、要么rejected:

    案例一:

    const promise = new Promise((resolve, reject) => {
        resolve('fulfilled...'); // 状态由 pending --> fulfilled
    });
    
    promise.then(res => {
        console.log(res); // 只会调用 onFulfilled
    }, err => {
        console.log(err); // 不会调用 rejected
    })
    // fulfilled
    

    案例二:

    const promise = new Promise((resolve, reject) => {
        reject('rejected...'); // 状态由 pending --> rejected
    });
    
    promise.then(res => {
        console.log(res); // 不会调用 onFulfilled
    }, err => {
        console.log(err); // 只会调用 rejected
    })
    // rejected
    

    案例三:

    const promise1 = new Promise((resolve, reject) => {
        resolve('fulfilled...'); // 状态先由 pending --> rejected
        reject('rejected...'); //  状态不会再变 pending --> rejected
    });
    
    promise1.then(res => {
        console.log(res); // 只会调用 onFulfilled
    }, err => {
        console.log(err); // 不会调用 rejected
    })
    // fulfilled
    
    const promise2 = new Promise((resolve, reject) => {
        reject('rejected...'); // 状态先由 pending --> rejected
        resolve('fulfilled...'); // 状态不会再变 pending --> rejected
    });
    
    promise2.then(res => {
        console.log(res); // 不会调用 onFulfilled
    }, err => {
        console.log(err); // 只会调用 rejected
    })
    // rejected
    

    通过以上三个案例,我们发现,promise状态一旦由pending提升为fulfilled或rejected就不会再改变了,只能单向变化;并且then方法中只会调用其中一个方法(成功的回调或者事变的回调),不会二者都调用。

    Promise.resolve(1)
      .then(2)
      .then(Promise.resolve(3))
      .then(console.log)
    
    // 输出 1
    

    Promise的then方法的参数期望是函数,传入非函数则会发生值穿透

  • Promise.prototype.catch(onRejected): catch在链式写法中可以捕获前面then中发送的异常

    promise.then(res => {
       console.log('haha');
       return 'haha'
    }).then(res => {
        throw new Error('hehe'); // 此处错误被捕获
    }).catch(err => {
        console.log(err)
    })
    
    promise.then(res => {
        console.log('haha');
        throw new Error('haha'); // 此处错误被捕获
    }).then(res => {
        console.log('hehe');
        throw new Error('hehe'); // 此处不会执行
    }).catch(err => {
        console.log(err)
    })
    

    catch一旦捕获到了一个then中的错误,后续的then方法就不会再执行下去了。其实,catch相当于then(null,onRejected),前者只是后者的语法糖而已,使用catch方法替代更好吧。

  • Promise.prototype.finally(onFinally): 无论当前promise的状态是完成(fulfilled)还是失败(rejected)都会执行的回调,并且返回新的promise对象。

    promise.then(res => {
        throw new Error('test');
    }).catch(errr => {
        console.log(err);
    }).finally(()=>{
        // 返回状态为(resolved 或 rejected)都会执行
        console.log('当前promise执行结束')
    });
    
    let isLoading = true;
    fetch(myRequest).then(function(response) {
        let contentType = response.headers.get("content-type");
        if(contentType && contentType.includes("application/json")) {
          return response.json();
        }
        throw new TypeError("Oops, we haven't got JSON!");
      })
      .then(function(json) { /* process your JSON further */ })
      .catch(function(error) { console.log(error); })
      .finally(function() { isLoading = false; });
    
  • Promise.prototype.constructor: 返回被创建的实例函数. 默认为 Promise 函数.

2.3 promise静态方法

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

  • Promise.resolve(value):返回一个状态由给定value(可以是普通值,也可以是promise对象)决定的Promise对象。如果value是thenable(即,带有then方法的对象),返回的Promise对象的最终状态由then方法执行决定,否则返回成功的promise对象(状态为fulfilled)

  • Promise.reject(reason):返回一个状态为失败(rejected)的Promise对象,并将给定的失败信息传递给对应的处理方法

  • Promise.all(iterable): 一损俱损,只要一个失败,就返回新的pomise对象,否则就等待所有的状态提升为fulfilied。“团结”

    用于处理多个promise对象的状态集合,集合中可以传入常数,当做成功的promise返回值。当集合中一旦有一个promise转改变为rejected,all的状态就变为rejected:

    const p1 = Promise.resolve(1);
    const p2 = Promise.resolve(2);
    const p3 = Promise.reject(3);
    const p4 = Promise.resolve(4);
    // 没有错误
    Promise.all([p1,p2,33, p4]).then(res => {
        console.log(res); // [1,2,33,4]
    })
    // 有错误
    Promise.all([p1,p2,p3, 33, p4]).then(res => {
        console.log(res); // 只要发现一个错误,就不会执行fulfilled方法
    }).catch(err => {
        console.log(err); // 执行错误,输出3
    })
    

    当集合中所有的promise状态都为fulfilled时,必须等待所有的promise执行完毕,即所有的promise对象状态都提升为fulfilled再返回新dePromise对象,执行then方法。

  • Promise.race(iterable): 一荣俱荣,只要一个状态改变(fulfilled或reject),就返回新的promise对象。“赛跑”

    用于处理多个promise对象的状态集合,集合中可以传入常数,当做成功的promise返回值。当集合中一旦有一个promise转改变为fulfilled或rejected,all的状态就提升:

    Promise.race([p1,p2,33,p4]).then(res=> {
        console.log(res); // 只会输出其中一个状态成功改变的,输出:1
    });
    
    Promise.race([p1,p2,p3, 33,p4]).then(res=> {
        console.log(res); // 虽然p3出错了,但是p1状态先提升,只会执行状态最先提升的,输出:1
    }).catch(err => {
        console.log(err);// 这里不会执行
    });
    
  • *Promise.any(iterable): 处于提案中。。。只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态。

    手动实现如下:

    Promise.any = function(arr) {
        var self = this;
        return new Promise(function(resolve, reject){
            var errors = [];
            var count = 0;
            var len = arr.length;
            for(var i=0;i<len;i++){
                // 只要有一个实例状态变为fulfilled,新的Promise状态就会改变为fulfilled
                self.resolve(arr[i]).then(function(res){
                    resolve(res);
                }, function(err){
                    errors[count] = err;
                    count++;
                    // 否则等待所有的rejected,新的Promise状态才会改变为rejected
                    if(count === len){
                        reject(errors)
                    }
                })
            }
        })
    }
    
  • *Promise.allSettled(iterable): ES2020将实现。。。 只有等到所有这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束。不关心结果,只关心有没有执行完毕,该方法返回的新的 Promise 实例,一旦结束,状态总是fulfilled,不会变成rejected

    手动实现如下:

    Promise.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 Promise(function(resolve, reject) {
           resolve(results)
        })
    }
    
  • *Promise.try():事实上,Promise.try就是模拟try代码块,就像promise.catch模拟的是catch代码块。

    需求的提出:

    不论函数f是同步函数还是异步操作,但是想用 Promise 来处理它:

    Promise.resolve().then(f).catch()
    

    ​ 但是,如果f是同步函数,那么它会在本轮事件循环的末尾执行。

    const f = () => console.log('now');
    Promise.resolve().then(f);
    console.log('next');
    // next
    // now
    

    有没有方法,让同步函数同步执行,异步函数异步执行,并且让它们具有统一的 API捕获错误:

    const f = () => console.log('now');
    (
      () => new Promise(
        resolve => resolve(f())
      )
    )();
    console.log('next');
    // now
    // next
    
    // 或者
    (async () => f())();
    console.log('next');
    // now
    // next
    

    Promise.try替代上面的代码:

    const f = () => console.log('now');
    Promise.try(f);
    console.log('next');
    // now
    // next
    

    手动实现如下:

    Promise.try = function(fn){
        if(typeof fn !== 'function') return;
        return new Promise(function(resolve, reject) {
            return resolve(fn());
        })
    }
    

2.3 promise属性

  • Promise.length:length属性,其值总是1(构造器参数的数目)
  • Promise.prototypePromise构造器的原型

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

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

运行时概念

  • 函数调用形成了一个栈帧 (call stack)。

  • 对象被分配在一个堆(heap)中,即用以表示一大块非结构化的内存区域。

  • 所有的异步操作都会进入队列(queue)中等待被执行。

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

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

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

事件循环机制

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

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

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

  • 宏任务:script(主代码块), setTimeout, setInterval, setImmediate, I/O, UI rendering,MessageChannel、setImmediate(Node.js 环境)。每次执行栈执行的代码就是一个宏任务

  • 微任务process.nextTick(nodejs相关), Promise, Object.observer, MutationObserver在当前宏任务(主线程) 执行结束后立即执行的任务

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

JS运行机制:

  • 执行栈中的宏任务(栈中没有就从事件队列中获取),一般首先执行普通代码块(script)

  • 执行过程中如果遇到微任务,就将它添加到微任务队列中

  • 当前宏任务执行完毕后,立即执行当前微任务队列中的所有微任务(依次执行)

  • 当前宏任务执行完毕,开始检查渲染,然后GUI线程接管渲染

  • 渲染完毕后,JS线程继续接管,开始下一个宏任务(从事件队列中获取),以此循环...

    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

  • 使用ES5之前的语法实现:
(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;
})();

  • 使用ES6 class语法实现
  // 判断变量否为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 })
      );
    }
  }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271