关于promise我在之前的文章已经应用过好几次,如[js高手之路]Node.js+jade+express+mongodb+mongoose+promise实现todolist,本文就来讲解下promise的常见用法.
为什么会有promise,他的作用是什么?
promise主要是为了解决js中多个异步回调难以维护和控制的问题.
什么是promise?
从图中,我们可以看出,Promise是一个函数,这个函数上有在项目中常用的静态方法:all, race, reject,resolve等,原型对象上有有catch, then等方法.也就是说,如果要调用catch和then方法,需要一个Promise的实例( new Promise ), 而静态方法可以用函数本身调用,如:Promise.all, Promise.race等,好了,至此,至少知道Promise是什么,但是还是不知道怎么用他提供的方法,接下来我们从一个需求开始,在javascript中,经常有这样的需求:
1,页面上有一个按钮,一个ul,点击按钮的时候,每隔1秒钟向ul的后面追加一个li, 一共追加10个,li的内容从0开始技术( 0, 1, 2, ....9 ),这个在我的博客文章中,出现好几次
2,每隔1秒钟输出递增的数字,如( 1, 2, 3, 4, 5 等 )
3,红绿灯( 红->(等2秒)->绿( 等2秒 )->黄( 等2秒 ) -> 红等,如此循环下去
4,node.js中的爬虫,爬文章url->爬文章内容->爬文章url->爬文章内容
好了,太多这类应用了,这类应用叫做异步回调( 执行完一件事,才能接着往下执行),如果出现多次,就会产生多层嵌套( 回调地狱 ),维护和控制异步过程非常的麻烦和困难,如:
每隔1秒钟输出递增的数字,如( 1, 2, 3 等 )
setTimeout(function () {
console.log(1);
setTimeout(function () {
console.log(2);
setTimeout(function () {
console.log(3);
}, 1000);
}, 1000);
}, 1000);
这还是3层,如果4层,5层。。。。,现在代码简单,可能感觉不到,如果console.log换成具体的业务逻辑,那就不得了,假如每个真实的业务逻辑有100行代码,5层嵌套。而业务逻辑中又有很多的嵌套匹配。你能想象吗?如果由于业务需求需要 调换业务的执行顺序?是不是很头疼?不用担心,Promise可以帮你非常灵活的调整:
function next( n ){
return new Promise( function( resolve, reject ){
setTimeout( function(){
resolve( n );
}, 1000 );
} );
}
next( 1 ).then( function( res ){
console.log( res );
return next( 2 );
} ).then( function( res ){
console.log( res );
return next( 3 );
} ).then( function( res ){
console.log( res );
} )
Promise的构造函数接收一个参数,是函数,并且传入两个参数:resolve,reject,分别表示异步操作执行成功后的回调函数和异步操作执行失败后的回调函数。按照标准来讲,其实resolve是将Promise的状态置为fullfiled,reject是将Promise的状态置为rejected,promise的常用用法.
一般是用一个函数嵌套,返回一个promise对象,为什么这么用?
因为then,catch方法需要一个Promise实例,才能把多个异步执行的操作,根据resolve和reject的执行状态一层层往下执行.
当我们执行next( 1 )的时候,在next函数中返回一个promise对象,一秒钟之后,通过resolve把n( 就是 1 )传递给then方法的第一个function,参数res就是resolve传递过来的数据,所以1秒钟后输出1,紧接着我在return next( 2 ),这个时候又调用了一次Promise对象,一秒钟后,通过resolve把n ( 就是 2 )传递给下一个then方法的第一个function, 参数res就收到n( 2 )的值,所以1秒钟后输出2。。。下面输出3的过程跟刚才分析的一样,有一点一定要注意,在then方法中的function 调用的next方法,一定要用return ,否则不会通过resolve把数据往下传递( 通俗点讲就是下一个异步操作,接收不到上一步的结果 ).
then中的function也可以return一个值,把一个值往下传递
function next( n ){
return new Promise( function( resolve, reject ){
setTimeout( function(){
resolve( n );
}, 1000 );
} );
}
next( 1 ).then( function( res ){
console.log( res );
return 2;
} ).then( function( res ){
console.log( res );
return 3;
} ).then( function( res ){
console.log( res );
} )
reject是把数据传递给then方法的第二个function处理,then方法可以接收2个参数
function next(){
return new Promise( function( resolve, reject ){
var num = Math.floor( Math.random() * 10 );
if ( num <= 5 ) {
resolve( num );
}else {
reject( new Error() );
}
} );
}
next( 1 ).then( function( res ){
console.log( res );
}, function( res ){
console.log( res );
} );
console.log( '正常执行' );
当随机数大于等于6的时候,我把一个错误往下抛,然后在then的第二个参数接收到,整个程序还是能够正常运行.
catch也是接收reject传递的数据
function next(){
return new Promise( function( resolve, reject ){
var num = Math.floor( Math.random() * 10 );
if ( num <= 5 ) {
resolve( num );
}else {
reject( new Error() );
}
} );
}
next( 1 ).then( function( res ){
console.log( res );
} ).catch( function( res ){
console.log( 'reject:' + res );
} );
console.log( '正常执行' );
Promise.all是等所有的异步资源都加载完毕之后,再执行代码,比如。页面有很多的插件库,一般都是要加载完毕之后,才能有特效效果。Promise.all主要解决的是多个异步模块的依赖问题,必须等大家都加载完毕之后,才执行( 说白了,就是等最慢的那个异步操作执行完了,再打印出结果 )
var count = 0;
function next() {
return new Promise(function (resolve, reject) {
var num = Math.floor(Math.random() * 10);
setTimeout( function(){
console.log( num );
resolve( `第${++count}随机到的值是${num}`);
}, 2000 );
});
}
Promise.all( [ next(), next(), next() ] ).then( function( res ){
console.log( res );
} );
三个next()异步操作执行完毕之后,才会一起把他们的resolve结果打印出来
Promise.race,只要最快的异步执行完毕之后,就执行then,不会等待其他的异步操作
Promise还有其他的用法,在项目中,这些是比较常用到的