Promise使用及自定义

573 阅读6分钟

1是什么?

  • 抽象表达:JS中解决异步编程的新的解决方案(原先异步编程的解决方式是使用回调函数,回调函数本身没有问题,问题是多个回调函数嵌套,容易形成回调地狱,Promise就是为了解决这个问题)
  • 具体表达
    • 语法上,Promise是一个构造函数,用来生成Promise实例对象。
    • 功能上,promise对象用来封装一个异步操作并可以获取其结果(成功、失败)进行相应的处理。

2为什么使用Promise?

  • 指定回调函数的方式更加灵活

    • 旧的:必须在启动异步任务之前指定
    • promise:启动异步任务、返回promise对象、给promise对象绑定回调函数
  • 支持链式调用,解决回调地狱问题

    • 回调地狱:回调函数嵌套调用,外部回调函数的执行结果是嵌套的回调执行的条件
    • 回调地狱的缺点:不便于阅读、不便于异常处理

6.3三种状态

pending、resolved、rejected

  • pending:可以转换为其他两个状态,一旦转换成这两种状态就不再改变了。(只有这两种状态改变,并且一个promise对象只能改变一次)
  • resolve:将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果value,作为参数传递出去;
  • reject:将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误的原因reason,作为参数传递出去。
  • resolve 和 reject 的作用域只有起始函数,不包括 then 以及其他序列;resolve 和 reject 并不能够使起始函数停止运行,别忘了 return。
  • 抛出错误也能将promise从pending状态改为rejected

6.4基本使用

  • Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数。在异步操作成功时调用resolve函数,失败时调用reject函数。
  • then方法:分别指定resolved状态和rejected状态的回调函数。
  • 第一个回调函数参数当promise对象的状态变为resolved时调用,可以拿到resolve()传过来的值value
  • 第二个回调函数参数当promise对象的状态变为rejected时调用,可以拿到reject()传过来的原因reason,可选的
  • 可以链式调用,前一个then()方法中的回调函数中又可能返回一个promise实例,这时候后面一个then()方法中的回调函数会等前一个promise实例的状态发生变化才会调用。
  • 如果我们的后续任务是异步任务的话,必须return 一个新的 promise 对象。如果后续任务是同步任务,只需 return 一个结果即可。
// 创建一个promise实例p
const p = new Promise((resolve, reject) => {
  setTimeout(() => {
    const time = Date.now();
    if(time % 2 == 0){
      resolve('成功,time='+time)
    }else{
      reject('失败,time='+time)
    }
  }, 1000);
})

p.then(value=>{
  console.log('成功的回调', value);
},reason=>{
  console.log('失败的回调', reason);
})

6.5API

(1)Promise构造函数

  • excutor函数:执行器(resolve,reject)=>{}
  • resolve函数:内部定义成功时我们调用的函数resolve(成功的结果)
  • reject函数:内部定义失败时我们调用的函数reject(失败的原因)

注意:excutor会在Promise内部立即同步调用

(2)Promise.prototype.then(onResolved,onRejected)

  • onResolved函数,成功的回调函数,value=>{}
  • onRejected函数,失败的回调函数,reason=>{}
  • 返回一个新的promise对象

(3)Promise.prototype.catch

  • 用于指定发生错误时的回调
  • 是Promise.prototype.then(undefined,onRejected)方法的别名
  • 接收一个函数参数,这个函数的参数是promise失败的原因

(4)Promise.resolve

  • value=>{}
  • value:成功的数据或promise对象
  • 返回一个成功/失败的promise对象
// 如果传入的参数为非Promise类型的对象,则返回的结果为成功的Promise对象
let p1 = Promise.resolve(111);
console.log(p1); // Promise {<fulfilled>: 111}

// 如果传入的参数为Promise对象,则参数的结果决定了resolve的结果
let p2 = Promise.resolve(new Promise((resolve, reject)=>{
  resolve("OK")
}))
console.log(p2); // Promise {<fulfilled>: "OK"}

let p3 = Promise.resolve(new Promise((resolve, reject)=>{
  reject("error");
}))
p3.catch(reason=>{
  console.log(reason);
})
console.log(p3); // Promise {<rejected>: "error"}

(5)Promise.reject

  • reason=>{}
  • 返回一个失败的promise对象
// Promise.reject, 不管参数是什么,返回一个失败的Promise
let p4 = Promise.reject(111);
console.log(p4); //Promise {<rejected>: 111}

let p5 = Promise.reject(new Promise((resolve, reject)=>{
  resolve();
}))
console.log(p5); //Promise {<rejected>: Promise}

(6)Promise.all

  • (promises)=>{},promises包含n个promise的数组
  • 多个 Promise 任务同时执行。如果全部成功执行,则以数组的方式返回所有 Promise 任务的执行结果。 如果有一个 Promise 任务 rejected,则只返回 rejected 任务的结果(如果多个任务失败,返回第一个任务失败的结果)。
//当所有的promise都成功
let p1 = new Promise((resolve, reject)=>{
  resolve('ok');
})
let p2 = Promise.resolve(111);
let p3 = Promise.resolve(222);
let result = Promise.all([p1, p2, p3]);
console.log(result);
// Promise {<pending>}
// 	[[Prototype]]: Promise
// 	[[PromiseState]]: "fulfilled"
// 	[[PromiseResult]]: Array(3)
//    0: "ok"
//    1: 111
//    2: 222

// 当有promise失败
let p1 = new Promise((resolve, reject)=>{
  resolve('ok');
})
let p2 = Promise.reject(111);
let p3 = Promise.resolve(222);
let result = Promise.all([p1, p2, p3]);
//Promise {<pending>}
//	[[Prototype]]: Promise
//  [[PromiseState]]: "rejected"
//  [[PromiseResult]]: 111

(7)Promise.race

  • (promises)=>{},promises包含n个promise的数组
  • 多个 Promise 任务同时执行,返回最先执行结束的 Promise 任务的结果,不管这个 Promise 结果是成功还是失败。
let p1 = new Promise((resolve, reject)=>{
  setTimeout(() => {
    resolve('ok');
  }, 1000);    
})
let p2 = Promise.resolve(111);
let p3 = Promise.resolve(222);
let result = Promise.race([p1, p2, p3]);
console.log(result);// 最先执行结束的是p2
// Promise {<pending>}
// [[Prototype]]: Promise
// [[PromiseState]]: "fulfilled"
// [[PromiseResult]]: 111

(8)finally()方法

  • 用于指定不管 Promise 对象最后状态如何,都会执行的操作。

(9)any()方法

  • 接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例返回。
  • 只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态。

6.6异常穿透与中断链式调用

(1)异常穿透

  • 当你使用Promise的then,进行链式调用的时候,可以在最后指定失败的回调
  • 前面任何操作出现了异常,都会传递到最后失败的回调中进行处理

(2)中断链式调用

  • 有且只有一种方式,返回一个pending状态的promise,return new Promise(()=>{})
  • 后面的then就不会执行

6.7手写Promise(*****)

class Promise{
    constructor(excutor){
      	// 初始状态
        this.promiseState = 'pending';
      	// 结果
        this.promiseResult = null;
        this.callback = [];

        const that = this;
        function resolve(data){
            //这里面的this指向的是window,拿外面的this保存在that中
            if(that.promiseState !== 'pending') return;
            that.promiseState = 'fulfilled';
            that.promiseResult = data;
            //执行成功的回调函数,回调函数不止一个,遍历
            that.callback.forEach((item)=>{
                setTimeOut(()=>{
                    item.onResolved(data);
                })
            })
        }

        function reject(data){
            if(that.promiseState !== 'pending') return;
            that.promiseState = 'rejected';
            that.promiseResult = data;
            //执行失败的回调函数
            that.callback.forEach((item)=>{
                setTimeout(() => {
                    item.onRejected(data);
                });
            })
        }

        try{
            // 同步调用执行器函数,并接受参数
            excutor(resolve, reject);
        }catch(e){
            reject(e);
        }
    }

    then(onResolved,onRejected){
        if(typeof onRejected !== 'function'){
            onRejected = reason => {
                throw reason;
            }
        }
        if(typeof onResolved !== 'function'){
            onResolved = value => value;
        }
        
        return new Promise((resolve, reject)=>{
            // 封装回调函数
            const callback = (type) =>{
                try {
                    let result = type(this.promiseResult);
                    if(result instanceof Promise){
                        result.then(v=>{
                            resolve(v);
                        },r=>{
                            reject(r);
                        })
                    }else{
                        resolve(result);
                    }
                } catch (error) {
                    reject(error);
                }
            }
    
            if(this.promiseState === 'fulfilled'){
                setTimeout(()=>{
                    callback(onResolved);   
                })         
            }
            if(this.promiseState === 'rejected'){
                setTimeout(()=>{
                    callback(onRejected);
                })
            }
            if(this.promiseState === 'pending'){
                // 使用数组保存回调函数,避免覆盖前面的回调函数
                this.callback.push({
                    onResolved: ()=>{
                        callback(onResolved);
                    },
                    onRejected: ()=>{
                        callback(onRejected);
                    }
                });
            }
        })
    }

    catch(onRejected){
        return this.then(undefined, onRejected);
    }

    static resolve(value){
        return new Promise((resolve, reject)=>{
            if(value instanceof Promise){
                value.then(v=>{
                    resolve(v);
                }, r=>{
                    reject(r);
                })
            }else{
                resolve(value);
            }
        })
    }

    static reject(value){
        return new Promise((resolve, reject)=>{
            reject(value);
        })
    }

    static all(promises){
        return new Promise((resolve, reject)=>{
            let count = 0;
            let arr = [];
            for(let i = 0; i< promises.length;i++){
                promises[i].then(v=>{
                    count++;
                    arr[i] = v;
                    if(count === promises.length){
                        resolve(arr);
                    }
                }, r=>{
                    reject(r)
                })
            }
        })
    }

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