爬虫基于回调和事件的方式去实现,回调也是被诟病已久的问题尤其是callback这种,无论是阅读还是调试都很费劲,甚至我们连代码的堆栈都看不到,这是一种反人类的写法,Promise来拜托这种痛苦的方式
传统方式实现动画效果:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Promise animation</title> <style> .ball{ width:40px; height:40px; border-radius: 20px;/**圆角**/ } .ball1{ background: red; } .ball2{ background: yellow; } .ball3{ background: green; } </style> <script src="./node_modules/bluebird/js/browser/bluebird.js"></script> </head> <body > <div class="ball ball1" style="margin-left: 0;"></div> <div class="ball ball2" style="margin-left: 0;"></div> <div class="ball ball3" style="margin-left: 0;"></div> <script type="text/javascript"> function init(){ //首先拿到这三个小球 var ball1 = document.querySelector(‘.ball1‘) var ball2 = document.querySelector(‘.ball2‘) var ball3 = document.querySelector(‘.ball3‘) /** * 动画函数 * @param {Object} ball 球 * @param {Object} distance 位置,把球移动到哪里 * @param {Object} cb 回调的callback */ function animate(ball,distance,cb){ alert(ball) //设定一个延时 setTimeout(function(){ //拿到球现在距离左边距的位置 var marginLeft = parseInt(ball.style.marginLeft,10) //判断左边距移动到和我们传入的distance的时候 //重叠,说明动画执行完毕 if(marginLeft===distance){ //直接调用回调函数 cb && cb() }else{//小球没有达到我们预期的位置 //球在我们期望位置的左侧 if(marginLeft<distance){ marginLeft++ }else{//在右侧 marginLeft-- } //调整球的样式 ball.style.marginLeft = marginLeft + ‘px‘ //不断重复去做,直到移动到我们所期望的位置 animate(ball,distance,cb) } },13) //时间越短,动画越流畅,1秒钟60帧 } //调用动画_传统方式来执行动画 animate(ball1,100,function(){//球从左侧向右侧移动100像素 animate(ball2,200,function(){ animate(ball3,300,function(){ //移动完毕之后继续移动ball3 animate(ball3,150,function(){ animate(ball2,150,function(){ animate(ball1,150,function(){ //end }) }) }) }) }) }) } </script> </body> </html>
运行结果如下:
Promise方式如下:
再使用Promise方法之前先导入npm install bluebird模块,之后再用script标签来引用
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Promise animation</title> <style> .ball{ width:40px; height:40px; border-radius: 20px;/**圆角**/ } .ball1{ background: red; } .ball2{ background: yellow; } .ball3{ background: green; } </style> <script src="./node_modules/bluebird/js/browser/bluebird.js"></script> </head> <body> <div class="ball ball1" style="margin-left: 0;"></div> <div class="ball ball2" style="margin-left: 0;"></div> <div class="ball ball3" style="margin-left: 0;"></div> <script type="text/javascript"> //首先拿到这三个小球 var ball1 = document.querySelector(‘.ball1‘) var ball2 = document.querySelector(‘.ball2‘) var ball3 = document.querySelector(‘.ball3‘) /** * 动画函数 * @param {Object} ball 球 * @param {Object} distance 位置,把球移动到哪里 * @param {Object} cb 回调的callback */ function animate(ball,distance,cb){ //设定一个延时 setTimeout(function(){ //拿到球现在距离左边距的位置 var marginLeft = parseInt(ball.style.marginLeft,10) //判断左边距移动到和我们传入的distance的时候 //重叠,说明动画执行完毕 if(marginLeft===distance){ //直接调用回调函数 cb && cb() }else{//小球没有达到我们预期的位置 //球在我们期望位置的左侧 if(marginLeft<distance){ marginLeft++ }else{//在右侧 marginLeft-- } //调整球的样式 ball.style.marginLeft = marginLeft + ‘px‘ //不断重复去做,直到移动到我们所期望的位置 animate(ball,distance,cb) } },13) //时间越短,动画越流畅,1秒钟60帧 } //如何用promise来执行动画呢,先安装一个库npm install bluebird //要安装在该页面同层的下面 function promiseAnimate(ball,distance){ return new Promise(function(resolve,reject){ function _animate(){ //设定一个延时 setTimeout(function(){ //拿到球现在距离左边距的位置 var marginLeft = parseInt(ball.style.marginLeft,10) if(marginLeft===distance){ //直接调用回调函数 resolve() }else{//小球没有达到我们预期的位置 //球在我们期望位置的左侧 if(marginLeft<distance){ marginLeft++ }else{//在右侧 marginLeft-- } //调整球的样式 ball.style.marginLeft = marginLeft + ‘px‘ //不断重复去做,直到移动到我们所期望的位置 _animate() } },13) //时间越短,动画越流畅,1秒钟60帧 } //启动调用 _animate() }) } promiseAnimate(ball1,100) .then(function(){ return promiseAnimate(ball2,200) }) .then(function(){ return promiseAnimate(ball3,300) }) .then(function(){ return promiseAnimate(ball3,150) }) .then(function(){ return promiseAnimate(ball2,150) }) .then(function(){ return promiseAnimate(ball1,150) }) </script> </body> </html>
运行效果同上。
在callback中,如果调换动画的执行顺序或者增加几个或者减少几个,需要重新更改代码;
如果用promise则是一个线性的控制无论是阅读还是维护的体验上都得到了提升,
这仅仅是在浏览器中的应用,在NodeJs中回调的场景更多也更残忍,
尤其是有远程API同步和数据库查询、文件读写操作的时候,Promise就能解决这些痛点
并且也远不止于此
了解Promise需要了解如下知识
1.ES6的Promise语言标准、Promise/A+规范
Promise是针对JavaScript中的异步场景中的解决方案(传统:回调、事件机制、订阅者、等)
Promise是一个对象和JavaScript中普通的对象没有什么区别,
同时它也是一种规范,异步操作约定了统一的接口,表示一个异步操作的最终结果,
以同步的方式来写代码,执行的操作是异步的,又保证了程序的执行顺序是同步的,
只有三种状态:未完成、已完成、失败
其中中间转换过程只能发生一次并且是不可逆的;
意思是指:要么从未完成到已完成,要么从未完成到失败。
那Promise/A+规范是什么呢?
升级版,行为标准 扩展了原来的规范,约定俗成的行为,总之是一个更为标准的
不同之处:
A+规范通过术语thenable来区分promise对象
A+定义onFulfiled/onRejected必须是作为函数来调用,而且调用过程必须是异步的
A+严格定义了then方法链式调用时onFulfilled/onRejected的调用顺序
then方法定义如下:
promiseObj.then(onFulfilled,onRejected) onFulfilled = function(value){ return promiseObj2 } onRejected = function(err){}
2.如何使用
链式调用,例如:
promiseAnimate(ball1,100) .then(function(){ return promiseAnimate(ball2,200) }) .then(function(){ return promiseAnimate(ball3,300) }) .then(function(){ return promiseAnimate(ball3,150) }) .then(function(){ return promiseAnimate(ball2,150) }) .then(function(){ return promiseAnimate(ball1,150) })
3.在什么场景下使用
只要是异步编程的地方都可以使用,业务场景简单不要为了使用Promise而使用Promise
Promise库有很多,例如:
bluebird
Q
then.js
es6-promise
ypromise
async
native-promise-only
等等