JavaScript基础:Promise相关

203 阅读3分钟

手写promise dizhi

Promise.resolve()

Promise.resolve(value)可以将任何值转成值为value,状态是fulfilled的Promise,但如果传入的值本身是Promise则会原样返回

Promise.resolve = function(value){
    // 如果是Promise则直接输出它
    if(value instanceof Promise){
        return value;
    }
    return new Promise(resolve=>resolve(value));
}

Promise.reject()

和Promise.resolve类似,Promise.reject会实例化一个rejected状态的Promise。但与Promise.resolve不同的是,如果给Promise.reject传递一个Promise对象,则这个对象会成为新Promise的值

Promise.reject = function(reason){
    return new Promise(resolve,reject)=>reject(reason));
}

Promise.all()

Promise.all的规则:

  • 传入的所有Promise都是fulfilled,则返回由他们的值组成的,状态为fulfilled的新Promise;
  • 只要有一个Promise是rejected,则返回rejected状态的新Promise,且它的值是第一个rejected的Promise的值;
  • 只要有一个Promise是pending,则返回一个pending状态的新Promise;
Promise.all = function(promiseArr){
    let index =0,result = [];
    return new Promise((resolve,reject)=>{
        promiseArr.forEach((p,i)=>{
            Promise.resolve(p).then(value=>{
                index++;
                result[i] = value;
                if(index === promiseArr){
                    resolve(result);
                }
            },err=>{
                reject(err);
            })
        })
    })
}

Promise.race()

Promise.race会返回一个由所有可迭代实例中第一个fulfilled或rejected的实例包装后的新实例。

Promise.race = function(promiseArr){
    return new Promise((resolve,reject)=>{
        promiseArr.forEach((p)=>{
            Promise.resolve(p).then((val)=>{
                resolve(val)
            },err=>{
                reject(err)
            })
        })
    })
}

Promise.allSettled()

Promise.allSettled的规则:

  • 所有Promise的状态都变化了,那么新返回一个状态是fulfilled的Promise,且它的值是一个数组,数组的每项由所有Promise的值和状态组成的对象;
  • 如果有一个是pending的Promise,则返回一个状态是pending的新实例;
Promise.allSettled = function(promiseArr){
    const result = []
    return new Promise((resolve,reject)=>{
        promiseArr.forEach((p)=>{
            Promise.resolve(p).then((val)=>{
                result.push({
                    value:val,
                    status:"fulfilled"
                })
                if(result.length === promiseArr.length){
                    resolve(result)
                }
            },err=>{
                result.push({
                    reason:err,
                    status:"rejected"
                })
                if(result.length === promiseArr.length){
                    resolve(result)
                }
            })
        })
    })
}

Promise.any()

Promise.any的规则:

  • 空数组或者所有Promise都是rejected,则返回状态是rejected的新Promise,且值为AggregateError的错误;
  • 只要有一个是fulfilled状态的,则返回第一个是fulfilled的新实例
  • 其他情况都会返回一个pending的新实例
Promise.any = function (promiseArr){
    let index =0;
    return new Promise((resolve,reject)=>{
        promiseArr.forEach(p=>{
            Promise.resolve(p).then(val=>{
                resolve(val)
            })
        },err=>{
            index++;
            if(index === promiseArr.lenght){
                reject(new AggregateError("All Promises were rejected"))
            }
        })
    })
}

实现sleep函数

const sleep = timeout => {
    return new Promise((resolve,reject) => {
        setTimeout(resolve,timeout)
    })
}

Generator

  1. Generator是一种更现代的异步解决方案,在JS语言层面支持了协程
  2. Generator的返回值是一个迭代器
  3. 这个迭代器需要手动调next才能一条一条执行yield
  4. next的返回值是{value,done},value是yield后面的表达式的值
  5. yield语句本身并没有返回值,下次调next的参数会作为上一个yield语句的返回值
  6. Generator自己不能自动执行,要自动执行需要引入其他方案,co模块是一个很受欢迎的自动执行方案
  7. thunk和co自动执行的方案思路有点类似,都是写一个局部的方法,这个方法会调用gen.next,同时这个方法本身又会传到回调函数或者promise的成功分支里面,异步结束后又继续调用这个局部方法,这个局部方法有调用gen.next,这样一致迭代,直到迭代器执行完毕
  8. async/await其实是Generator和自动执行器的语法糖,写法和实现原理都类似co模块的promise模式。
  function co(g){
        const it = g();
        const exec = (res)=>{
            if(res.done){
                return res.value
            }
            return res.value.then(val=> exec(it.next(val))).catch(err=>exec(it.throw(err)))
        };
        exec(it.next())
   }

Promise重试次数

function retry(getDate,time,interval){
    return new Promise((resolve,reject)=>{
        function exec(){
            getDate().then(resolve).catch(err=>{
                if(time ===0){
                    reject(err)
                }else{
                    time--;
                    setTimeout(exec,interval)
                }
            })
        }
        
        exec()
    })
}

Promise 手写

class MyPromise{
    constructor(excutor){
        this.state = 'pending';
        this.fulfilledEvent = [];
        this.rejectedEvent = [];
        let resolve = (result)=>{
            if(this.state !== 'pending') return;
            this.state = 'fulfilled';
            clearTimeout(this.timer);
            this.timer = setTimeout(()=>{
                this.fulfilledEvent.forEach(item=>{
                    if(typeof item ==='function'){
                        item(result)
                    }
                })
            },0)
        }
        let reject = (result)=>{
            if(this.state !=='pending') return;
            this.state='rejected';
            clearTimeout(this.timer);
            this.timer = setTimeout(()=>{
                this.rejectedEvent.forEach(item=>{
                    if(typeof item === 'function'){
                        item(result);
                    }
                })
            },0)
        }

        try{
            excutor(resolve,reject)
        }catch(err){
            reject(err)
        }
    }
    then(resolveFn,rejectFn){
        return new MyPromise((resolve,reject)=>{
            this.fulfilledEvent.push(()=>{
                let x= resolveFn();
                x instanceof MyPromise ? x.then(resolve,reject): resolve()
            })
            this.rejectedEvent.push(rejectFn);
        })
    }
}

Promise 串行

let arrA = [
    ()=>{
        return new Promise(resolve=>{
            setTimeout(()=>{
                console.log(1);
                resolve(1)
            },1000)
        })
    },
    ()=>{
        return new Promise(resolve=>{
            setTimeout(()=>{
                console.log(2);
                resolve(2)
            },2000)
        })
    },
    ()=>{
        return new Promise(resolve=>{
            setTimeout(()=>{
                console.log(3);
                resolve(3)
            },3000)
        })
    }
];

function execFun(arr){
    return new Promise(resolve=>{
        const result = []; 
        arr.reduce((pre,cur)=>{
           return pre.then(cur).then((data)=>{
               result.push(data)
           })
        },Promise.resolve()).then(()=>{
            resolve(result);
        })
    })
};

let sings = [].concat(arrA,arrA,arrA);
execFun(sings);