码迷,mamicode.com
首页 > 其他好文 > 详细

promise对象的理解

时间:2021-03-05 13:04:26      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:结果   用户   for   理解   typeof   html   fun   pen   cal   

参考:
https://juejin.cn/post/6844903629187448845
https://juejin.cn/post/6844904088963022856
https://www.cnblogs.com/mfyngu/p/13880867.html
 
Promise对象
Promise是异步编程的一种解决方法。是一个构造函数,用来生成Promise实例。这个构造函数里有两个参数,分别是:resolve(成功之后的回调函数)、reject(失败之后的回调函数)。
promise实例生成之后,可以用then的方法分别指定resolved状态和rejected状态的回调函数,then方法接受两个回调函数作为参数。第一个回到函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。两个函数都是可选的,不一定提供。*通过。then指定回调函数的时候,成功的回调函数必须传,失败的回调函数可以省略(then方法接受的两个回调函数被调用的时机通过Promise对象的状态决定)
 
举个??:
 
 
let p = new Promise(function(resolve, reject){
    //做一些异步操作
    setTimeout(function(){
        console.log(‘执行完成Promise‘);
        resolve(‘要返回的数据可以任何数据 例如接口返回数据‘);
    }, 2000);
});

console.log(‘中间‘);
p.then(res => {
    console.log(‘then成功的回调是:‘, res)
})
console.log(‘结尾‘)


打印输出:
中间
结尾
执行完成Promise
// 两秒后输出
then成功的回调是:要返回的数据可以任何数据 例如接口返回数据

上面的例子说明了promise可以很好的处理异步操作。因为promise的then方法,是在 resolve函数执行之后才调用的。

promise还有哪些实用呢?

首先理解一下promise

 

1、当promise的转态是pending的时候,可能会转化到fulfilled或者是rejected转态

 

2、当promise状态是fulfilled的时候,不能转化为其他的转态,必须返回一个value,并且这个value保持不变。

 

3、当promise的状态是rejected的时候,也不能转化为其他的状态,必须返回一个失败的原因。
因为promise,所以叫promise。
 

 

promise是用来解决回调地狱的问题的。可以链式调用,可以嵌套调用。
1、promise是一个构造函数,用来生成Promise实例。支持异步。这个构造函数里接受一个函数作为参数,该函数有两个参数,分别是:resolve(异步操作成功之后的回调函数)、reject(异步操作失败之后的回调函数)。
2、Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数
3、then方法返回的是一个新的Promise实例。,可进行链式调用。后一个回调函数会等前一个Promise对象的状态发生变化的时候,才会被调用
4、catch()方法,是.then(null, rejection)或者是.then(undefined, rejection)的别名,用于指定发生错误时的回调函数。
5、finally()方法,用于指定不管Promise对象最后的状态如何,都会执行的操作,在执行完then或catch指定的回调函数
6、all()方法,用于将多个Promise实例,包装成一个新的Promise实例,该方法接受一个数组作为参数,p1、p2、p3都是Promise实例,当三者的状态都变为fulfilled,all()的实例的状态才会变成fulfilled,只要有一个rejected,all的实例的状态就会变成rejected
7、race()方法,同样也是包装成一个新的Promise实例,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。
8、any()方法,与race的方法很像,只有一点不同,就是不会因为某个Promise变成rejected装填而结束。
 

回调地狱

很烦。

apiA({
    handeleSucess(resA){
    apiB({
        handleSucess(resB){
            apiC({
                handleSucess(resc)
            })
        }
    })
}})

简单的实现promise

1.1简单实现promise

const PENDING = ‘PENDING‘;
const RESOLVED = ‘RESOLVED‘;
const REJECTED = ‘REJECTED‘;

class PromiseA {
    constructor(executor) {
        this.status = PENDING; // 宏变量, 默认是等待态
        this.value = undefined; // then方法要访问到所以放到this上
        this.reason = undefined; // then方法要访问到所以放到this上
this.onResolvedCallbacks = [];// 专门存放成功的回调函数 this.onRejectedCallbacks = [];// 专门存放成功的回调函数

let resolve = (value) => { if (this.status === PENDING) {// 保证只有状态是等待态的时候才能更改状态 this.value = value; this.status = RESOLVED; // 监听回调函数,需要让成功的方法依次执行 this.onResolvedCallbacks.forEach(fn => fn()); } }; let reject = (reason) => { if (this.status === PENDING) { this.reason = reason; this.status = REJECTED; // 需要让失败的方法依次执行 this.onRejectedCallbacks.forEach(fn => fn()); } }; // 执行executor传入我们定义的成功和失败函数:把内部的resolve和reject传入executor中用户写的resolve, reject try { executor(resolve, reject); } catch(e) { console.log(‘catch错误‘, e); reject(e); //如果内部出错 直接将error手动调用reject向下传递 } } then(onfulfilled, onrejected) {
    //解决onFufilled,onRejected不是函数的问题,给一个默认的回调函数。
    onFufilled = typeof onFufilled === ‘function‘?onFufilled:y=>y
    onRejected = typeof onRejected === ‘function‘?onRejected:err=>{ throw err ;}
if (this.status === RESOLVED) {
            onfulfilled(this.value);
        }
        if (this.status === REJECTED) {
            onrejected(this.reason);
        }
        // 处理异步的情况,pending的时候,把任务加入callback队列
        if (this.status === PENDING) {
            // this.onResolvedCallbacks.push(onfulfilled); 这种写法可以换成下面的写法,多包了一层,这叫面向切片编程,可以加上自己的逻辑
            this.onResolvedCallbacks.push(() => {
                // TODO ... 自己的逻辑
                onfulfilled(this.value);
            });
            this.onRejectedCallbacks.push(() => {
                // TODO ... 自己的逻辑
                onrejected(this.reason);
            });
        }
    }
}

首先new Promise时,实例化一个对象,会执行constructor函数,然后传给promise的函数发送异步请求,会将异步请求发送到异步栈里, 接着调用了promise对象的then的属性,注册请求成功和注册请求失败的回调函数。

至此,同步任务执行结束。进入到异步任务里,异步执行resolve函数,此时state为pending状态,把转态置Resolved状态,并一一执行成功清单里面的任务。

调用:

let promise = new PromiseA((resolve, reject) => {
    setTimeout(() => {
        resolve(‘xxx‘);
    }, 1000);
});
// 发布订阅模式应对异步 支持一个promise可以then多次
promise.then((res) => { 
    console.log(‘成功的结果1‘, res);
}, (error) => { 
    console.log(error);
});

promise.then((res) => { 
    console.log(‘成功的结果2‘, res);
}, (error) => { 
    console.log(error);
});

 

1.2是先promise.then的链式调用

这个时候已经实现了基本的promise了。可以支持异步。但是还不支持链式调用。
下面实现链式调用。
1、then方法必须返回的必须是一个promise,这样才能保证链式调用
2、如果then内部的回调函数执行结果依然是一个promise,那就把这个promise的结果resolve出去
3、任何一个promise必须是resolve之后才能刚走到它的then方法,从而创建下一个promise。
4、什么时候走成功回调,then返回一个普通值或者一个成功的promise。
5、什么时候走失败的回调,?返回一个失败的promise,或者抛出异常,
 
class PromiseA {
    constructor(executor) {
        this.status = PENDING; // 宏变量, 默认是等待态
        this.value = undefined; // then方法要访问到所以放到this上
        this.reason = undefined; // then方法要访问到所以放到this上
        // 专门存放成功的回调函数
        this.onResolvedCallbacks = [];
        // 专门存放成功的回调函数
        this.onRejectedCallbacks = [];
        let resolve = (value) => {
            if (this.status === PENDING) { // 保证只有状态是等待态的时候才能更改状态
                this.value = value;
                this.status = RESOLVED;
                // 需要让成功的方法一次执行
                this.onResolvedCallbacks.forEach(fn => fn());
            }
        };
        let reject = (reason) => {
            if (this.status === PENDING) {
                this.reason = reason;
                this.status = REJECTED;
                // 需要让失败的方法一次执行
                this.onRejectedCallbacks.forEach(fn => fn());
            }
        };
        // 执行executor 传入成功和失败:把内部的resolve和 reject传入executor中用户写的resolve, reject
        try {
            executor(resolve, reject); // 立即执行
        } catch (e) {
            console.log(‘catch错误‘, e);
            reject(e); //如果内部出错 直接将error 手动调用reject向下传递
        }
    }
    then(onfulfilled, onrejected) {
        onFufilled = typeof onFufilled === ‘function‘ ? onFufilled : y => y;
        onRejected = typeof onRejected === ‘function‘ ? onRejected : err => {throw err};
        // 上面的两个判断用来解决then函数中的参数不是函数的问题,

      
// 为了实现链式调用,创建一个新的promise let promise2 = new Promise((resolve, reject) => { if (this.status === RESOLVED) { // 执行then中的方法 可能返回的是一个普通值,也可能是一个promise,如果是promise的话,需要让这个promise执行 // 使用宏任务把代码放在一下次执行,这样就可以取到promise2,为什么要取到promise2? 这里在之后会介绍到 setTimeout(() => { try {
              // 下面两行是一个规整化(用来处理then里面可能是promise的),
              // 若不考虑规整化,可直接使用下面注释的一行,
              // onfufilled(this.reason)
                        let x = onfulfilled(this.value);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) { // 一旦执行then方法报错就走到下一个then的失败方法中
                        console.log(e);
                        reject(e);
                    }
                }, 0);
            }
            if (this.status === REJECTED) {
                setTimeout(() => {
                    try {
                        let x = onrejected(this.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                }, 0);
            }
            // 处理异步的情况
            if (this.status === PENDING) {
                // 这时候executor肯定是有异步逻辑
                this.onResolvedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onfulfilled(this.value);
                            // 注意这里传入的是promise2的resolve和reject
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (e) {
                            reject(e);
                        }
                    }, 0);
                });
                this.onRejectedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onrejected(this.reason);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (e) {
                            reject(e);
                        }
                    }, 0);
                });
            }
        });

        return promise2;
    }
}

  1. 为什么外面要包一层setTimeout?:因为Promise本身是处理一个异步方法,必须得在执行栈执行完了在去取他的值,所以,所有的返回值都得包一层异步setTimeout。
  2. resolvePromise是什么?因为then返回的是promise对象的,如果是promise对象,需要将他拆解,直到不是promise对象,取出其中的值。
 
resolvePromise函数,用来规程化的,用来处理then内部回调函数是promise函数的,
function resolvePromise(promise2, x, resolve, reject) {
    if((typeof x === ‘object‘ && x != null) || typeof x === ‘function‘) {
        // 有可能是promise, 如果是promise那就要有then方法
        let then = x.then;
        if (typeof then === ‘function‘) { // 到了这里就只能认为他是promise了
            // 如果x是一个promise那么在new的时候executor就立即执行了,就会执行他的resolve,那么数据就会传递到他的then中
            then.call(x, y => {// 当前promise解析出来的结果可能还是一个promise, 直到解析到他是一个普通值
                resolvePromise(promise2, y, resolve, reject);// resolve, reject都是promise2的
            }, r => {
                reject(r);
            });
        } else {
            // 出现像这种结果 {a: 1, then: 1} 
            resolve(x);
        }
    } else {
        resolve(x);
    }
}

1.3catch方法

catch方法其实就是没有成功回调的then方法,

//异常处理 用于指定发生错误时的回调函数。
//promise抛出一个错误,就被catch()方法指定的回调函数捕获
Promise.prototype.catch = function (onRejected) {
  return this.then(undefined, onRejected)
}

1.4 finally方法

Promise.finally()方法,用于指定不管Promise对象的最后的状态如何,都会执行的操作;
finally方法的回调函数不接受任何参数,这意味着没有办法知道,前面的Promise状态到底是fufilled还是rejected,这表明,finally方法里面的操作,应该与状态是无关的,不依赖Promise的执行结果。因此可以绑定此事件在当前的promise实例的then方法上,在成功的时候回调传入的函数,在失败的时候也进行回调传入的参数。
/**
 *  finally 函数 promise m每次执行后都会进行执行
 * @param {*} cb 
 */
PromiseA.prototype.finally = function (cb) {
  //finally 传入函数,无论成功或者失败都会执行 
  return this.then(data => {
    //Promise.resolve 可以等待这个promise完成
    return Promise.resolve(cb().then(() => data))
  }, err => {
      //失败的时候也执行
    return Promise.reject(cb().then(() => {
      throw err
    }))
  })
}

1.5 Promise.all()方法

Promise.all可用于接收一个数组作为参数,参数可以不是数组,但是必须有Iterator接口,且返回的每个成员都是Promise的实例,他的结果是根据传入的数据进行变化的

//all方法(获取所有的promise,都执行then,把结果放到数组,一起返回),所有的成功才会成功,一个失败就会失败
Promise.all = function(promiseList){
  let arr = []
  let i = 0
  function processData(index,data){
    arr[index] = data
    i++
    if(i == promises.length){
      resolve(arr)
    }
  }
  return new Promise((resolve,reject)=>{
    for(let i=0;i<promises.length;i++){
      promises[i].then(data=>{
        processData(i,data)
      },reject)
    }
  })
}

其原理就是将参数中的数组取出遍历,每当执行成功都会执行processData方法,processData方法就是用来记录每个Promise的值和它对应的下标,当执行的次数等于数组长度时就会执行resolve,把arr的值给then。这里会有一个坑,如果你是通过arr数组的长度来判断他是否应该resolve的话就会出错,为什么呢?因为js数组的特性,导致如果先出来的是1位置上的值进arr,那么0位置上也会多一个空的值,所以不合理。

 

1.5 race方法,

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

 



 

promise对象的理解

标签:结果   用户   for   理解   typeof   html   fun   pen   cal   

原文地址:https://www.cnblogs.com/jwenming/p/14481446.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!