promise链式调用的各种使用场景

在我们平时开发代码的过程中,经常回碰到回调函数嵌套回调函数的情况,因为下一步的操作要依赖上一步操作异步返回的结果。举个栗子,我们用setTimeout函数来模拟回调函数:

setTimeout(function(){
    setTimeout(function(){
        setTimeout(function(){
            ...
        },100)
    },100)
},100)

像这种嵌套一两个还好,如果嵌套多了,写代码的人写着头疼,看代码的更是看着头疼,结果无疑是灾难性的。为了解决这种问题,es6为我们引入了Promise

网上介绍promise语法的文章很多,本文将从另外一个角度,通过各种各样的使用场景来告诉大家如何使用Promise

1,基本用法
本场景介绍最常用的一种情况,即只有一层回调嵌套的情况。

new Promise(function(resolve, reject){
    setTimeout(function(){
        console.log('time ok')
        resolve('return value')
    }, 100)
}).then(function(value){
    console.log(value)
},function(err){
    console.log(err)
})
console.log('end')
/**
运行结果:

end
time ok
return value
*/

本场景为promise最基础的使用,为了让初学者能够接着往下看,下面介绍一下promise的一些基本概念:

  • Promise对象有三种状态:resolved(完成),pending(进行中),rejected(失败)
  • Promise初始化时需要传入一个回调函数,初始化完成后,该回调函数会立即开始执行,此时Promise对象的状态为pending
  • Promise初始化的时候出入的回调函数有两个形参,第一形参是resolve函数,第二个行参是reject函数。resolve函数可以将当前Promise对象的状态从pending变成resolved;reject函数可以将当前Promise对象的状态从pending变成rejected
  • Promise对象的then函数,有两个参数,第一个参数是一个回调函数,在Promise对象从pending变为resolved的时候执行,回调函数的形参为Promise内部通过resolve函数传过来的值,如上个例子中,“return value” 就是通过这种方式传递过来的;第二个参数也是一个回调函数,在Promise对象从pending变为rejected的时候执行,回调函数的形参为Promise对象内部通过reject函数传过来的值,第二个参数可以省略

2,多层嵌套,链式调用
Promise的产生就是为了优雅的解决这种问题,如何将文章开头写的那种灾难性的写法用Promise写出来呢,直接上代码:

new Promise(function(resolve, reject){
    setTimeout(function(){
        console.log('time ok')
        resolve('value 1')
    }, 100)
}).then(function(value){
    return new Promise(function(resolve, reject){
        setTimeout(function(){
            console.log('接收参数:'+ value)
            resolve('value 2')
        }, 400)
    })
}).then(function(value){
    return new Promise(function(resolve, reject){
        setTimeout(function(){
            console.log('接收参数:'+ value)
            resolve('value 3')
        }, 200)
    })
}).then(function(value){
    console.log('接收参数:'+ value)
})
console.log('end')
/**
执行结果:

end
time ok
接收参数:value 1
接收参数:value 2
接收参数:value 3
*/

可以看出,与setTimeout函数定时的时间长短无关,函数严格按照从上到下的顺序执行,如果还有跟多的的回调可以链式的接着写then,这样比以前的n重嵌套的写法易读和易维护很多

then函数会返回一个全新的Promise对象,这个Promise对象可以自己通过Promise来new一个,如上面的例子,可以通过Promise.resolve()或者Promise.reject()来构造,甚至可以不手动返回,then函数也会自动返回一个Promise对象,先看如下代码:

var p1 = new Promise(function(resolve, reject){
    setTimeout(function(){
        console.log('time ok')
        resolve('value 1')
    }, 100)
})
var p2 = p1.then(function(value){
    console.log('接收参数:' + value)
})
console.log('p2:' + p2)
var p3 = p2.then(function(value){
    console.log('suc:' + value)
},function(err){
    console.log('fail:' + err)
})
console.log('p1和p2是否相同:' + (p1 == p2))
console.log('p2和p3是否相同:' + (p2 == p3))
/**
执行结果:

p2:[object Promise]
false
false
time ok
suc:undefined
*/

从执行结果中两个判断结果都为false来看,then函数返回的Promise已经和原Promise不指向同一个对象,可见then函数是重新new了一个Promise对象;从最后一个输出为"suc:undefined"来看,p2中的then函数虽然没有返回任何东西,then函数也自动返回了一个Promise.resolve()对象

我们接着看下面一段代码:

new Promise(function(resolve,reject){
    setTimeout(function(){
        resolve('value1')
    },100)
}).then(function(value){
    console.log('接收参数:' + value)
    return Promise.resolve('value2')
}).then(function(value){
    console.log('接收参数:' + value)
    return 'value3'
}).then(function(value){
    console.log('接收参数:' + value)
    return Promise.reject('fail')
}).then(null, function(err){
    console.log('错误:' + err)
})
console.log('end')
/**
执行结果:

end
接收参数:value1
接收参数:value2
接收参数:value3
错误:fail
*/
  • Promise.resolve(‘suc’) 等同于new Promise(resolve=>resolve(‘suc’))
  • Promise.reject(‘fail’)等同于new Promise((resolve,reject)=>reject(‘fail’))
  • then函数的两个回调函数参数中,里面return object相当于return Promise.resolve(object),在下一个then中会触发then函数的第一个resolve的回调函数

3,链式调用中捕获异常
可以通过Promise对象的catch函数来捕获整个链式函数中的错误,catch(function(err){ })函数相当于then(null,function(err){ }),也会返回一个Promise对象。链式调用中错误会一直网下层抛,知道有一个catch或者then函数的reject函数捕获到这个错误,往后才会继续执行resolve,否则会一直reject下去

new Promise(function(resolve, reject){
    setTimeout(function(){
        console.log('time ok')
        resolve('value 1')
    }, 100)
}).then(function(value){
    return new Promise(function(resolve, reject){
        setTimeout(function(){
            console.log('1接收参数:'+ value)
            resolve('value 2')
        }, 400)
    })
}).then(function(value){
    return new Promise(function(resolve, reject){      
        setTimeout(function(){
            console.log('2接收参数:'+ value)
            resolve('value 3')
        }, 200)
        new kk()
        reject('wrong1')
    })
}).then(function(value){
    console.log('3接收参数:'+ value)
}).then(function(value){
    console.log('4接收参数:'+ value)
},function(err){
    console.log('2接收错误:'+ err)
}).catch(function(err){
    console.log('catch:' + err)
}).then(function(value){
    console.log('5接收参数:'+ value)
})
console.log('end')
/**
执行结果:

end
time ok
1接收参数:value 1
2接收错误:ReferenceError: kk is not defined
5接收参数:undefined
2接收参数:value 2
*/

从执行结果可看出,“3接收参数”和“4接收参数”这两个console写在resolve回调中,别并没有被执行,直到“2接收错误”捕获到这个错误,后面的“5接受参数”才执行。在上面的例子中,如果将“2接收错误”的reject回调函数去掉,则错误会在catch中捕获
4,前置多个回调函数均执行成功,才执行后面的代码
我们在开发过程中,偶尔也会碰到一种情况,就是某段代码需要依赖两个或者两个以上的回调函数的结果才能继续执行,这种情况使用Promise同样能够胜任,看如下代码:

let p1 = new Promise(function(resolve, reject){
    setTimeout(function(){
        resolve('value 1')
    },100)
})
let p2 = new Promise(function(resolve, reject){
    setTimeout(function(){
        resolve('value 2')
    },400)
})
let p3 = new Promise(function(resolve, reject){
    setTimeout(function(){
        resolve('value 3')
    },100)
})
Promise.all([p1, p2, p3]).then(function(results){
    console.log('接收结果:' + results)
},function(errs){
    console.log(err)
})
console.log('end')
/**
执行结果:

end
接收结果:value 1,value 2,value 3
*/

Promise.all()适合用于多个前置回调函数都完成了才去执行then()成功的操作

5,前置多个回调函数中只要一个执行成功,就执行后面的代码
这种情况指的是一段代码只要前置的多个回调函数有一个执行成功就开始执行,这种情况在平时并不多见,我们通过下面的代码来了解一下:

let p1 = new Promise(function(resolve, reject){
    setTimeout(function(){
        console.log('发送结果:value 1')
        resolve('value 1')
    },100)
})
let p2 = new Promise(function(resolve, reject){
    setTimeout(function(){
        console.log('发送结果:value 2')
        resolve('value 2')
    },400)
})
let p3 = new Promise(function(resolve, reject){
    setTimeout(function(){
        console.log('发送结果:value 3')
        resolve('value 3')
    },200)
})
Promise.race([p1, p2, p3]).then(function(results){
    console.log('接收结果:' + results)
},function(errs){
    console.log(err)
})
console.log('end')
/**
执行结果:

end
发送结果:value 1
接收结果:value 1
发送结果:value 3
发送结果:value 2
*/

从执行结果中可以看出,由于value1的settimeout时间最短,第一个将结果resolve了出去,后面的then函数中的resolve回调立马就能执行并且接收到参数。随后两外两个Promise再按照定时器的时间长短分别将结果输出,只是不在触发then函数的resolve回调。

Promise.race()的作用也是同时执行多个回调,和all不同的是,只要有一个回调执行成功即调用后面的then函数,后面执行成功的回调将不再触发then()

  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值