之前怎么用回调解决异步的问题:
function f(callback){
setTimeout(function(){
callback && callback();
});
}
f(function(){
console.log(1);
f(function(){
console.log(2);
f(function(){
console.log(3);
f(function(){
console.log(4);
f(function(){
console.log(5);
f(function(){
console.log(6);
})
})
})
})
})
})
使用promise实现相同的效果
//使用promise实现相同的效果
function f2(){
return new Promise(resolve=>{//参数传入一个回调函数
setTimeout(function(){
//时执行函数
resolve();
},1000)
})
}
f2()//只有返回Promise实例,才能.then
.then(function(){
console.log(11);
return f2();
})
.then(function(){
console.log(22);
return f2();
})
.then(function(){
console.log(33);
return f2();
})
.then(function(){
console.log(44);
return f2();
})
.then(function(){
console.log(55);
return f2();
})
.then(function(){
console.log(66);
return f2();
})

对比回调与Promise的流程控制
首先是回调
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>index</title>
<style>
.box{
width:100px;
height:100px;
background:lightgreen;
transition:all 1s;
color:#fff;
text-align:center;
line-height:100px;
font-size:40px;
}
</style>
</head>
<body>
<div class="box">哦</div>
<button id="btn">开始</button>
<script>
//动画
function move(el,x,y,cb){
el.style.transform=`translate(${ x }px, ${ y }px)`;
setTimeout(function(){
cb && cb();
},1000);
}
//获取元素
let box=document.querySelector(".box");
let btn=document.querySelector("#btn");
//绑定事件
btn.addEventListener("click",e=>{
//使用回调完成动画
move(box,100,100,function(){
move(box,200,200,function(){
move(box,100,300,function(){
move(box,0,0,function(){
console.log("移动结束!");
})
})
})
})
})
</script>
</body>
</html>
实现的效果

使用Promise来实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>index</title>
<style>
.box{
width:100px;
height:100px;
background:lightgreen;
transition:all 1s;
color:#fff;
text-align:center;
line-height:100px;
font-size:40px;
}
</style>
</head>
<body>
<div class="box">哦</div>
<button id="btn">开始</button>
<script>
//动画
function move(el,x,y){
return new Promise(resolve=>{
el.style.transform=`translate(${ x }px, ${ y }px)`;
setTimeout(function(){
resolve();
},1000);
})
}
//获取元素
let box=document.querySelector(".box");
let btn=document.querySelector("#btn");
//绑定事件
btn.addEventListener("click",e=>{
//使用Promise完成动画
move(box,100,100)
.then(function(){
return move(box,200,200);
})
.then(function(){
return move(box,100,300);
})
.then(function(){
return move(box,0,0);
})
.then(function(){
console.log("移动结束!");
})
})
</script>
</body>
</html>
实现一个图片的加载;设置第一张图片加载1s之后加载第二张图片
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>index</title>
<style>
img{width:200px;}
</style>
</head>
<body>
<script>
//设置一个函数,把图片的url地址作为参数
function createImg(url){
//实例化promise对象
return new Promise(resolve=>{
let img=new Image();//建立图像对象
img.src=url;//设置图片的地址
document.body.appendChild(img);//把图片节点插入到body中
setTimeout(function(){
resolve();//图片加载完成后执行resolve
},1000);
})
}
createImg("1.jpg")
.then(function(){
return createImg("2.jpg")
})
.then(function(){
return createImg("3.jpg")
});
</script>
</body>
</html>

信任问题
//信任问题演示
//回调
function method(cb){
setTimeout(function(){
cb && cb();
//因为某些bug导致某个函数多执行了一次
cb && cb();
},1000);
}
//promise
function method2(){
return new Promise(resolve=>{
setTimeout(function(){
resolve();
//resolve成功调用一次之后,后面的不会再执行
resolve();
},1000);
})
}
控制反转
//回调
function method(cb){
setTimeout(function(){
cb && cb.call({a:1,b:2});//执行回调,但是添油加醋
},1000);
}
//promise
function method2(){
return new Promise(resolve=>{
setTimeout(function(){
resolve();//调用的resolve都是自己写的,改善了控制反转的问题
},1000);
})
}
错误处理
function fn(val){
//第二个参数代表失败时做的事情
return new Promise((resolve,reject)=>{
if(val){
resolve();
}else{
reject();
}
})
}
fn(false)
.then(()=>{
console.log("成功");
},()=>{
console.log("失败");
})

错误处理回调可以传入参数
function fn(val){
//第二个参数代表失败时做的事情
return new Promise((resolve,reject)=>{
if(val){
resolve();
}else{
reject("404");
}
})
}
fn(false)
.then(()=>{
console.log("成功");
},e=>{
console.log(e);
})

resolve也可以传递参数,但是只能传一个,不能传两个
function fn(val){
//第二个参数代表失败时做的事情
return new Promise((resolve,reject)=>{
if(val){
resolve({a:1},{b:2});
}else{
reject("404");
}
})
}
fn(true)
.then((obj1,obj2)=>{
console.log(obj1);
console.log(obj2);
},e=>{
console.log(e);
})

使用实例的catch方法,可以捕获错误
如果返回的是错误,则下面必须有对错误的捕获处理,否则代码不会执行,会被跳过
function fn(val){
//第二个参数代表失败时做的事情
return new Promise((resolve,reject)=>{
if(val){
resolve("这是数据");
}else{
reject("404");
}
})
}
fn(true)
.then(data=>{
console.log(data);
return fn(false);//失败,抛出错误
})
.then(()=>{
console.log("这里没有对错误的处理,因此不会执行");
})
.catch(e=>{//捕获错误,执行代码
console.log(e);
})

如果在捕获错误之前,存在对错误的处理,那么catch不会再执行
function fn(val){
//第二个参数代表失败时做的事情
return new Promise((resolve,reject)=>{
if(val){
resolve("这是数据");
}else{
reject("404");
}
})
}
fn(true)
.then(data=>{
console.log(data);
return fn(false);//失败,抛出错误
})
.then(()=>{
console.log("这里没有对错误的处理,因此不会执行");
})
.then(()=>{
},e=>{
console.log("这里对错误进行了处理,下面的catch不会被执行了");
})
.catch(e=>{//捕获错误,执行代码
console.log(e);
})

catch之后还可以继续then,如果再次抛出错误,也需要在之后进行错误处理
function fn(val){
//第二个参数代表失败时做的事情
return new Promise((resolve,reject)=>{
if(val){
resolve("这是数据");
}else{
reject("404");
}
})
}
fn(true)
.then(data=>{
console.log(data);
return fn(false);//失败,抛出错误
})
.then(()=>{
console.log("这里没有对错误的处理,因此不会执行");
})
.catch(e=>{//捕获错误,执行代码
console.log(e);
return fn(false);//再次抛出错误
});
最后抛出的错误没有捕获,因此报错

finally 不管成功或失败,都会执行
function fn(val){
//第二个参数代表失败时做的事情
return new Promise((resolve,reject)=>{
if(val){
resolve("这是数据");
}else{
reject("404");
}
})
}
fn(true)
.then(data=>{
console.log(data);
return fn(false);//失败,抛出错误
})
.catch(e=>{//捕获错误,执行代码
console.log(e);
})
.finally(()=>{
console.log("finally执行一些收尾工作");
})

Promise的状态
panding 进行中
fulfilled 成功
reject 失败

Promise.all()
把多个promise实例,包装成一个新的promise实例
如果所有promise结果都是成功,则返回成功,所有promise返回的数据以数组形式统一返回,且顺序一一对应
如果有一个promise决议为失败,则返回失败,且把失败的信息返回
如果是空数组,则立即决议为成功
//模拟需要多个请求数据才能进行下一步的情况
function data1(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log("data1加载成功");
resolve("data1");//传递参数
},1000)
})
}
function data2(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log("data2加载成功");
resolve("data2");//传递参数
},1000)
})
}
function data3(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log("data3加载成功");
resolve("data3");//传递参数
},1000)
})
}
function data4(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log("data4加载成功");
resolve("data4");//传递参数
},2000)
})
}
//全部成功的情况
let res=Promise.all([data1(),data2(),data3(),data4()]);
res
.then(data=>{
console.log(data);//接收上面传递过来的所有参数
})

//模拟需要多个请求数据才能进行下一步的情况
function data1(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log("data1加载成功");
resolve("data1");//传递参数
},1000)
})
}
function data2(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
reject("data2 err");//数据2请求失败
},1000)
})
}
function data3(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log("data3加载成功");
resolve("data3");//传递参数
},1000)
})
}
function data4(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log("data4加载成功");
resolve("data4");//传递参数
},2000)
})
}
//全部成功的情况
let res=Promise.all([data1(),data2(),data3(),data4()]);
res
.then(data=>{
console.log(data);//接收上面传递过来的所有参数
},e=>{
console.log(e);//有错误执行这句并立刻返回错误信息,正确的数据不会返回
})

//模拟需要多个请求数据才能进行下一步的情况
function data1(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log("data1加载成功");
resolve("data1");//传递参数
},1000)
})
}
function data2(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
reject("data2 err");//数据2请求失败
},1000)
})
}
function data3(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log("data3加载成功");
resolve("data3");//传递参数
},1000)
})
}
function data4(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log("data4加载成功");
resolve("data4");//传递参数
},2000)
})
}
//全部成功的情况
let res=Promise.all([]);
res
.then(()=>{
console.log("决议成功");//空数组直接决议为成功
},e=>{
console.log(e);//有错误执行这句并立刻返回错误信息,正确的数据不会返回
})

相同的功能,使用ES6之前的语法,不使用promise.all()要如何实现:
//不使用promise.all()
let count=0;
function fn(){
if(count<4) return;
console.log("数据全部接收成功");
}
function data1(){
setTimeout(()=>{
console.log("data1加载成功");
count++;
fn();
},2000)
}
function data2(){
setTimeout(()=>{
console.log("data2加载成功");
count++;
fn();
},2000)
}
function data3(){
setTimeout(()=>{
console.log("data3加载成功");
count++;
fn();
},2000)
}
function data4(){
setTimeout(()=>{
console.log("data4加载成功");
count++;
fn();
},2000)
}
data1();
data2();
data3();
data4();

如果有数据接收失败
//不使用promise.all()
let count=0;
let err=false;
function fn(){
if(err) {
console.log("有数据接收失败");
return;
}
if(count<4) return;
console.log("数据全部接收成功");
}
function data1(){
setTimeout(()=>{
console.log("data1加载成功");
count++;
fn();
},2000)
}
function data2(){
setTimeout(()=>{
console.log("data2加载失败");
err=true;
fn();
},2000)
}
function data3(){
setTimeout(()=>{
console.log("data3加载成功");
count++;
fn();
},2000)
}
function data4(){
setTimeout(()=>{
console.log("data4加载成功");
count++;
fn();
},2000)
}
data1();
data2();
data3();
data4();

Promise.race()
数组中只要有一个决议为成功,则立马决议为成功,并把值传递过来
//promise.race()
function data1(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log("data1成功") ;
resolve("data1");
},1000)
})
}
function data2(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log("data2成功") ;
resolve("data2");
},500)
})
}
function data3(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log("data3成功") ;
resolve("data3");
},1000)
})
}
function data4(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log("data4成功") ;
resolve("data4");
},1000)
})
}
let res=Promise.race([data1(),data2(),data3(),data4()]);
res
.then(data=>{
console.log(data);//输出最早成功的数据
},e=>{
console.log(e);
})

如果有错误,也会立即输出err信息
//promise.race()
function data1(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log("data1成功") ;
resolve("data1");
},1000)
})
}
function data2(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log("data2失败") ;
reject("err2");
},500)
})
}
function data3(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log("data3成功") ;
resolve("data3");
},1000)
})
}
function data4(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log("data4失败") ;
resolve("err4");
},1000)
})
}
let res=Promise.race([data1(),data2(),data3(),data4()]);
res
.then(data=>{
console.log(data);//输出最早成功的数据
},e=>{
console.log(e);
})

如果传入空数组,则程序被挂起
//promise.race()
function data1(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log("data1成功") ;
resolve("data1");
},1000)
})
}
function data2(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log("data2失败") ;
reject("err2");
},500)
})
}
function data3(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log("data3成功") ;
resolve("data3");
},1000)
})
}
function data4(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log("data4失败") ;
resolve("err4");
},1000)
})
}
let res=Promise.race([]);
res
.then(data=>{
console.log(data);//输出最早成功的数据
},e=>{
console.log(e);
})

如果不使用ES6的promise.race(),实现效果如下
//不使用promise.race()
let flag=false;
function fn(data){
if(flag) return;
flag=true;//有请求则返回true
console.log(data);
}
function data1(){
setTimeout(()=>{
console.log("data1成功") ;
fn({name:1})
},500)
}
function data2(){
setTimeout(()=>{
console.log("data2成功") ;
fn({name:2})
},600)
}
function data3(){
setTimeout(()=>{
console.log("data3成功") ;
fn({name:3})
},1000)
}
function data4(){
setTimeout(()=>{
console.log("data4成功") ;
fn({name:4})
},2000)
}
data1();
data2();
data3();
data4();

Promise.resolve() 不管传递什么值进去,都会包装成一个promise实例
//promise.resolve()
//传递一个普通值
let p1=new Promise(resolve=>{
console.log("p1决议成功");
})
let p2=Promise.resolve("p2成功");//传递一个普通的值,直接决议为成功
//传递一个promise实例
let p11=new Promise(resolve=>{
console.log("p11决议成功");
})
let p22=Promise.resolve(p11);//传递一个promise实例,使得p11和p22相等
p11.then(data=>void console.log(data));
console.log(p11===p22);
//定义一个thenable对象obj
let obj={
then(cb){
console.log("成功")
cb("成功啦")
},
oth(){
console.log("失败")
}
}
//Promise.resolve(obj) 传递一个thenable对象
Promise.resolve(obj).then(data=>{
console.log(data)
})

Promise.reject()
不管传递什么值,拿到的都是传入的值,不会进行操作和处理
//promise.reject()
//传递一个thenable对象obj
Promise.reject({then(){console.log("err")}})
.then(function(){
console.log("我不会被执行");
},e=>{
console.log(e)
})

resolve是异步任务,会在所有同步任务完成后执行
console.log(1);
let p=new Promise(resolve=>{
console.log(2);
resolve();//调用resolve相等于调用.then,是异步执行,在所有同步完成后执行
console.log(3);
})
console.log(4);
p.then(()=>{
console.log(5);
})
console.log(6);

把同步任务转为异步任务
function fn(cb){
//返回一个决议成功的实例,并异步执行
return Promise.resolve(cb).then(cb=>cb());
}
fn(()=>{
console.log("我从同步变成了异步");
return 1+1;
}).then(res=>{
console.log(res);//拿到return的值
})
console.log("我是同步");

小案例
页面中有多个板块,需要所有图片加载完成后再显示
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>es6 promise</title>
</head>
<style>
img{height:100px;}
</style>
<body>
<script>
const loadImg=(src)=>{
return new Promise((resolve,reject)=>{
let img=new Image();
img.src=src;
//图片加载成功
img.onload=()=>{
resolve(img)
}
//图片加载失败
img.onerror=(e)=>{
reject(e)
}
//注意这种写法是错误的,因为赋值时直接被调用,还没有等待图片加载已经执行完毕了
// img.onload=resolve(img)
// img.onerror=reject(e)
})
}
const imgs=["1.jpg","2.jpg","3.jpg"];
// map通过遍历把src作为参数传入,循环调用loadImg,获取到返回的image对象
Promise.all(imgs.map(src=>loadImg(src)))
.then(res=>{
console.log(res);
//遍历插入DOM
res.forEach(item=>{
document.body.appendChild(item)
})
})
</script>
</body>
</html>

失败的情况
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>es6 promise</title>
</head>
<style>
img{height:100px;}
</style>
<body>
<script>
const loadImg=(src)=>{
return new Promise((resolve,reject)=>{
let img=new Image();
img.src=src;
//图片加载成功
img.onload=()=>{
resolve(img)
}
//图片加载失败
img.onerror=(e)=>{
reject(e)
}
//注意这种写法是错误的,因为赋值时直接被调用,还没有等待图片加载已经执行完毕了
// img.onload=resolve(img)
// img.onerror=reject(e)
})
}
const imgs=["1.jpg","22.jpg","3.jpg"];
// map通过遍历把src作为参数传入,循环调用loadImg,获取到返回的image对象
Promise.all(imgs.map(src=>loadImg(src)))
.then(res=>{
console.log(res);
//遍历插入DOM
res.forEach(item=>{
document.body.appendChild(item)
})
})
.catch(e=>{
console.log(e)
})
</script>
</body>
</html>

来源:https://www.cnblogs.com/chenyingying0/p/12578285.html