之前在慕课网上学习nodejs时接触到了promise这个东东,感觉还不错,于是就想系统的学习一下。但很快就发现网上关于promise的资料有点少,于是就萌生了写有关promise的系列博客的想法。由于这个系列的博客我也是边学边写,难免会出现纰漏,所以有误的地方还请大家多多指正。
基础篇主要介绍什么是promise、promise的基本API。下面就进入正题。
一、与Promise相关的概念
1.什么是Promise/Promise有什么用
首先来看定义——Promise是js针对异步场景的解决方案,它可以以同步的方式编写代码,执行的操作却是异步的。
举个栗子,假如我们要实现一个动画——有三个小球,分别为红色、黄色、绿色,最后展示的效果是绿球先移动一段距离,结束移动后黄球移动,黄球移动结束后红球移动,红球移动一段距离后再移回原位,红球归位后黄球移动归位,黄球移动归位后绿球移动归位,在黄球归位以后动画结束。
不知道在看完整个动画的流程以后,大家对该动画的实现有什么想法,下面分享一下我的代码。首先是html部分
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>promise animation</title>
<link rel="stylesheet" type="text/css" href="ball.css">
</head>
<body>
<div id="balla" class="ball" style="left:0"></div>
<div id="ballb" class="ball" style="left:50px"></div>
<div id="ballc" class="ball" style="left:100px"></div>
<!-- <script type="text/javascript" src="bluebird.min.js"></script> -->
<script type="text/javascript" src="ball.js"></script>
</body>
</html>
这里大家需要在下方引入bluebird库的部分。目前的实例还不需要用到promise库。但由于Promise是ES6中引入的概念,为了防止某些运行环境不支持promise,我们采用bluebird库进行实现。之后的例子会详细介绍如何获得及使用bluebird库。
那么,首先如何获取bluebird库呢?我是用npm来获取的。在node.js运行环境中,输入npm install bluebird -g来获取bluebird库,运行完成之后的bluebird库目录结构如下所示
进入到js文件夹,我们会看到
有browser与release两个文件夹,browser文件夹中放置的是在浏览器端使用的bluebird库,就类似于jQuery,打开该文件夹,我们可以看到
接下来就可以把想要使用的库文件拷贝到相应位置进行引用。
下面来看一下css代码
*{margin:0;padding:0;}
.ball{
position:absolute;
width:40px;
height:40px;
border-radius:20px;
}
#balla{
background-color: red;
}
#ballb{
background-color: yellow;
}
#ballc{
background-color: green;
}
最后显示在浏览器的样子就是这样子的
那么,如果用原始的回调的方式进行编写,代码就应该想这样子
var balla=document.querySelector("#balla");
var ballb=document.querySelector("#ballb");
var ballc=document.querySelector("#ballc");
//ball传入小球的id,distance为移动的距离
function animateBall(ball,distance,callback){
setTimeout(function(){
//获取当前小球的位置
var position=parseInt(ball.style.left,10);
if(position==distance){
callback();
}else if(position<distance){
position++;
ball.style.left=position+"px";
animateBall(ball,distance,callback);
}else if(position>distance){
position--;
ball.style.left=position+"px";
animateBall(ball,distance,callback);
}
},13);
//13毫秒每帧
}
animateBall(ballc,500,function(){
animateBall(ballb,450,function(){
animateBall(balla,400,function(){
animateBall(balla,0,function(){
animateBall(ballb,50,function(){
animateBall(ballc,100,function(){
});
});
});
});
});
});
我们可以看到,在最后的回调部分,噼里啪啦的写了好几行,目前的需求写起来还算比较简单,但如果回调相当之多肿么办?这样子嵌套着写也不方便日后的维护,而且回调函数真正的问题在于它剥夺了我们使用 return 和 throw 这些关键字的能力,顺便也剥夺了我们使用链式编程的权力。于是乎,js便引入了promise来解决这个问题。
2.Promise构造函数
<1>promise构造函数接受一个函数作为参数,该函数包含两个参数,分别是resolve方法与reject方法
var promise=window.Promise;//获取全局promise对象
function promiseAnimate(ball,distance){
return new promise(function(resolve,reject){
function _animateBall(){
setTimeout(function(){
var position=parseInt(ball.style.left,10);
if(distance<0){
reject(new Error("距离不能为负数"));
}else if(position==distance){
resolve("完成移动");
}else if(position<distance){
position++;
ball.style.left=position+"px";
_animateBall();
}else if(position>distance){
position--;
ball.style.left=position+"px";
_animateBall();
}
},13);
};
_animateBall();
});
}
在分析代码之前,我们需要了解promise的特点
<2>promise的特点
promise对象有三种状态,分别为“未完成”(pending)、“已完成”(fullilled或者叫做resolved)、“失败”(rejected)。
只有异步操作的结果可以决定当前是哪一种状态。
一旦状态改变,就不会再变,任何时候都可以得到这个结果,并且状态只可能从pending变为resolved,或者从pending变为rejected
<3>构造函数分析
如果异步操作成功,则用resolve方法把promise对象的状态从pending转为resolved;如果失败,则从pending转为rejected。
下面是用最简单的promise进行编写。由于本节只简单介绍一下promise,所以还没有展示如何获取并处理错误。这些将在下一篇博客中为大家详细讲解。
promiseAnimate(ballc,500).then(function(){
return promiseAnimate(ballb,450)
}).then(function(){
return promiseAnimate(balla,400)
}).then(function(){
return promiseAnimate(balla,0)
}).then(function(){
return promiseAnimate(ballb,50)
}).then(function(){
return promiseAnimate(ballc,100)
}).then(function(value){
console.log(value)
/*前面的每个.then方法中函数的参数都可以是value,并且也可以console出来“完成移动”*/
});
二、基本API
promise对象的基本API有:
promise.resolve()、promise.reject()、promise.prototype.then()、promise.prototype.catch()、promise.all()
到此为止,我们的promise浅析的第一部分就结束了,下一章将讲解promise基本API的使用。敬请期待。最后为大家分享一下promise系列博客主要参考的网站http://liubin.org/promises-book/