别人不会告诉你的JavaScript知识:Promise 链式调用的另一种使用方式

为啥突然要记录下这个Promise链式调用呢?我觉得有必要强调一下。

在网络上大多数对Promise链式调用都是说下面这个代码(代码块1)

new Promise((resolve,reject)=>{
    resolve({data:1})
}).then(res=>{
 return res;
})
.then(res=>{
 return res;
})
.then(res=>{
 return res;
})
//......省略100个then
.then(res=>{
 return res;
})
.then(res=>{
 return res;
})

这种then不断接下去的一种形式,其实大多数情况下够用了。


最近在学习axios的实现,遇到了一种很有意思的链式调用方式,场景如下:

  1. let arr=[请求拦截器,请求拦截器,发起请求,响应拦截器,响应拦截器]
  2. arr的元素格式如下:{resolve:()=>{},reject:()=>{}} ,这是所有数组元素的实现协议,并且函数必须返回Promise.reject或则Promise.resolve

目标:

  1. 按顺序执行所有arr里面的resolve函数,
  2. 如果中途遇到reject情况可以,在arr最后一个函数执行后catch错误信息
  3. 如果都通过情况,可以通过then获取最终结果
  4. 支持异步和同步

 

实现代码:

var arr = [];//函数链,
var result = 1;//被函数链操作的数据
for (let i = 0; i < 5; i++) {
    arr.push({
        resolve: () => {
            if (i % 2 == 0) {
                return new Promise((resolve, reject) => {
                    setTimeout(()=>{
                        result+=1;
                        resolve({data:result,respone:{code:0}});
                    },200)
                  //  reject({respone:{code:1}}) 如果是错误就触发reject外卖可以获取到
                })
            }
            else {
                result += 1;
                return { data: result }
            }
        },
        reject: () => {
            result += 1;
            return { data: result }
        }
    })
}

let promise = Promise.resolve({ data: "1" });
while (arr.length) {
    let { resolve, reject } = arr.shift();
    console.log(resolve, reject)
    /**
     * then方法的第一个参数是resolved状态的回调函数,
     * 第二个参数(可选)是rejected状态的回调函数。
     */

    /**
     * then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。
     * 因此可以采用链式写法,即then方法后面再调用另一个then方法。
     */
    promise = promise.then(resolve, reject)  
}


promise.then((resolve, reject) => {
    console.log("resolve", resolve) // {data: 6,respone:{code:0}}
}).catch(e=>{
    console.log("err",e)
})


上面数组里面的reject函数可以捕获到上一个原生的错误信息,并返回给下一个元素,具体可以如下实现

 



var arr = [];//函数链,
var result = 1;//被函数链操作的数据
for (let i = 0; i < 5; i++) {
    arr.push({
        resolve: () => {
            if (i % 2 == 0) {
                return new Promise((resolve, reject) => {
                    // setTimeout(()=>{
                    //     result+=1;
                    //     resolve({data:result,respone:{code:0}});
                    // },200)
                    reject({respone:{code:1,index:i}}) //如果是错误就触发reject外卖可以获取到
                })
            }
            else {
                result += 1;
                return { data: result }
            }
        },
        reject: (e) => {
            console.log("上一个原生的错误",e)
            result += 1;
            //========重点看这里
            //如果想下个元素不进入reject,可以返回resolve
            //但是这样也会有弊端,不会触发最后一个原生的catch,也无法处理异常
            return Promise.reject({ data: result,error:"reject的信息" })
        }
    })
}

let promise = Promise.resolve({ data: "1" });
while (arr.length) {
    let { resolve, reject } = arr.shift();
    console.log(resolve, reject)
    /**
     * then方法的第一个参数是resolved状态的回调函数,
     * 第二个参数(可选)是rejected状态的回调函数。
     */

    /**
     * then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。
     * 因此可以采用链式写法,即then方法后面再调用另一个then方法。
     */
    promise = promise.then(resolve, reject)  
}


promise.then((resolve, reject) => {
    console.log("resolve", resolve) // {data: 6,respone:{code:0}}
}).catch(e=>{
    console.log("err",e)
})

 

深入解说:

  1. 本质上:这段代码和代码块1的功能是一样的,实际执行的程序和最终的效果依旧是then.then.then这种结果
  2. 区别是:在代码层面解决了链式里面所有函数的依赖,不需要把函数都写在then里面一层接一层的堆叠代码
  3. 减少函数和promise.then的解耦

以下代码扮演中介者的角色,把promise.then和对应的函数糅合到一块,生成如代码块1那样的程序,解除promise.then和对应函数的耦合,让他们不需要相互认识,让开发者专心写对应的函数功能&&不需要关系执行过程和次序

while (arr.length) {
//...

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章