1.什么是Promise
简单的说“Promise对象用于表示一个异步操作的最终状态(完成或者失败)以及其返回的值”
关于异步与同步,简单地说可以以同步任务会阻塞程序执行(alter、for、.......),而异步任务不会阻塞程序执行(setTimeout 、fs.readFile 、......)
2.首先我们比较传统的回调方式与promise
预备知识点:
-
then()方法是异步执行。就是当.then()前的方法执行完后再执行then()内部的程序,这样就避免了,数据没获取到等的问题。
-
箭头函数
var f = (参数)=>{ 代码块}
var ff = (x,y)=>{ return x+y }
*******************************
箭头函数的一个用处是简化回调函数。
// 正常函数写法
[1,2,3].map(function (x) {
return x * x;
});
// 箭头函数写法
[1,2,3].map(x => x * x);
- resolve()是指Promise成功调用时执行的事件
// 比较传统的回调方式与promise
// -------------------------------------------------------
// 回调
// 方法 用于请求数据(模拟)
// function f(cb) {
// setTimeout(function() {
// cb && cb();
// }, 1000);
// }
// 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
// 方法 用于请求数据(模拟)
function f() {
return new Promise(resolve => {
setTimeout(function() {
resolve();
}, 2000);
})
}
f()
.then(function() {
console.log(1);
return f();//返回后会以这个promise实例去执行,只是单单输出1后就马上输出2
})
.then(function() {
console.log(2);
});
.then(function() {
console.log(4);
return f();
})
.then(function() {
console.log(3);
return f();
})
.then(function() {
console.log(5);
return f();
})
.then(function() {
console.log(6);
});
传统的回调方式不仅层层叠叠,而且不容易时修改回调顺序。在这个环境下,产生了Promise,它正是为了解决多重嵌套回调函数而提出的,使用Promise后,回调函数条理更加清晰,同时如果当需要修改调用顺序时,只需要挪动下then函数的位置就可以了。
3.认识对比回调与Promise的流程控制
预备知识:
- 模板字符串(使用反引号的字符串)
- 箭头函数
html代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
#el {
width: 100px;
background: green;
transition: all 1s;
color: white;
line-height: 100px;
text-align: center;
font-size: 40px;
}
</style>
</head>
<body>
<div id="el">哦</div>
<button id="btn">开始</button>
<script src="./main.js"></script>
</body>
</html>
JS代码
// 动画
// function moveTo(el, x, y, cb) {
// el.style.transform = `translate(${x}px, ${y}px)`;
// setTimeout(function() {
// cb && cb();
// }, 1000);
// }
// let el = document.querySelector('div');
// document.querySelector('button').addEventListener('click', e => {
// moveTo(el, 100, 100, function() {
// moveTo(el, 200, 200, function() {
// moveTo(el, 30, 20, function() {
// moveTo(el, 100, 300, function() {
// moveTo(el, 130,20, function() {
// moveTo(el, 0, 0, function() {
// console.log('移动结束!');
// });
// });
// });
// });
// });
// });
// });
// promise
function moveTo(el, x, y) {
return new Promise(resolve => {
el.style.transform = `translate(${x}px, ${y}px)`;
setTimeout(function() {
resolve();
}, 1000);
});
}
let el = document.querySelector('div');
document.querySelector('button').addEventListener('click', e => {
moveTo(el, 100, 100)
.then(function() {
console.log('第一次移动');
return moveTo(el, 200, 200);
})
.then(function() {
console.log('第二次移动');
})
.then(function() {
console.log('第二次移动');
});
});
4.信任问题
Promise的信任问题很少书籍有做说明,我也只是初步了解。大家可以通过下面的代码感受下resolve()只可以执行一次。
// 信任问题
// 第三方的某个库
function method(cb) {
// 未按所想的预期执行回调
setTimeout(function() {
// 讲道理应该是现在该调用回调了
cb && cb();
// 但是?? 好像这个库有bug啊 emm 被多调用了一次
cb && cb();
}, 1000);
}
// promise一但被确定为成功或者失败 就不能再被更改
function method() {
return new Promise(resolve => {
setTimeout(function() {
// 成功
resolve();
resolve();
}, 1000);
});
}
// 控制反转
function method(cb) {
// 未按所想的预期执行回调
setTimeout(function() {
// 执行回调 但是添油加醋
cb && cb.call({a: 1, b: 2});
}, 1000);
}
function method(cb) {
return new Promise(resolve => {
setTimeout(() => {
resolve(); // 调用的resolve全为自己所写书写的流程 很大程度上改善了反转控制的问题
}, 1000);
});
}
5.错误处理
通过之前的例子我们可以感受到resolve()正确处理的效果,下面讲讲的是reject()错误处理的应用。
function f(val) {
return new Promise((resolve, reject) => {
if (val) {
resolve({ name: '小明' });//只可以传递一个参数
} else {
reject('404');
}
});
}
// then(resolve, reject)
// then方法中的第二个回调 失败时候做的事
// f(false)
// .then((data) => {
// console.log(data)
// }, e => {
// console.log(e);
// })
//----------------------------------------
// catch
// 使用实例的catch方法 可以捕获错误
// f(true)
// .then(data => {
// console.log(data);
// return f(false);
// })
// .then(() => {
// console.log('我永远不会被输出');//该then方法中没有第二个回调,所以不会对错误进行处理
// })
// .then(() => {
// })
// .catch(e => {
// console.log(e);
// return f(false) ;//这个错误处理没有进行处理,页面会报错处理,这个问题ES文档没有详细说明,不过有不少库可以处理。
// });
//----------------------------------------
// finally
// 不论成功还是失败 finally中的内容一定会执行,可以用来进行收尾工作
f(true)
.then(data => {
console.log(data);
return f(false);
})
.catch(e => {
console.log(e);
return f(false);
})
.finally(() => {
console.log(100);
});
6. 浅谈Promise三种状态
7.感受下Promise.all()方法,主要用于管理多个函数的执行状态
在以前,我们如果要管理多个函数,并且要统计函数的执行情况是要按下面这样
//不是用Promise.all
let count = 0;用于统计四个方法都结束了
let err = false;
function func() {
if (count < 4) return;
if (err) {
// ....
}
console.log('全部拿到了 !');
}
function getData1() {
setTimeout(() => {
console.log('第一条数据加载成功');
count ++;
func();
}, 1000);
}
function getData2() {
setTimeout(() => {
console.log('第二条数据加载成功');
count ++;
func();
}, 1000);
}
function getData3() {
setTimeout(() => {
console.log('第三条数据加载成功');
count ++;
func();
}, 1000);
}
function getData4() {
setTimeout(() => {
console.log('第四条数据加载成功');
count ++;
func();
}, 1000);
}
getData1();
getData2();
getData3();
getData4();
为了解决这个状况,我们可以使用Promise.all方法。all()中如果参数是空数组就默认为正确状况,出现正确或错误的Promise状况都会返回到数组中。
// Promise.all方法可以把多个promise实例 包装成一个新的promise实例
// Promise.all([ promise1, promise2 ]) : Promise
// 模拟需要多个请求的数据 才能进行下一步操作的情况
function getData1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('第一条数据加载成功');
resolve('data1');
}, 1000);
});
}
function getData2() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('第二条数据加载成功');
resolve('data2');
}, 1000);
});
}
function getData3() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('第三条数据加载成功');
resolve('data3');
}, 1000);
});
}
function getData4() {
return new Promise((resolve, reject) => {
setTimeout(() => {
// console.log('第四条数据加载成功');
reject('data4 err');
}, 500);
});
}
let p = Promise.all([]);
p.then(arr => {
console.log(arr);
}, e => {
console.log(e);
});
8.感受下Promise.race()方法,主要用于管理多个函数的竞赛状态(用于一个请求有响应了,马上去执行下一步的程序)。
一旦有一个Promise决议为成功或者失败,就会立马把Promise的值传递过去使实例的状态改变。race()参数为空数组是会处于挂载状态。
// 不使用pormise.race
let flag = false;
function func(data) {
if (flag) return;
flag = true;
console.log(data);
}
function getData1() {
setTimeout(() => {
console.log('第一条数据加载成功');
func({name: 'xiaoming'});
}, 500);
}
function getData2() {
setTimeout(() => {
console.log('第二条数据加载成功');
func({name: 'xiaohong'});
}, 600);
}
getData1();
getData2();
// Promise.race([ promise1, promise2 ]) : Promise
// function getData1() {
// return new Promise((resolve, reject) => {
// setTimeout(() => {
// console.log('第一条数据加载成功');
// reject('err');
// }, 500);
// });
// }
// function getData2() {
// return new Promise((resolve, reject) => {
// setTimeout(() => {
// console.log('第二条数据加载成功');
// resolve('data2');
// }, 1000);
// });
// }
// function getData3() {
// return new Promise((resolve, reject) => {
// setTimeout(() => {
// console.log('第三条数据加载成功');
// resolve('data3');
// }, 1000);
// });
// }
// let p = Promise.race([getData1(),getData2(),getData3()]);
// p.then(data => {
// console.log(data);
// }, e => {
// console.log(e);
// })
9.同步任务转为异步任务
一般事件执行情况下,同步任务会比异步任务先执行。所以可以通过Promise.resolve的性质进行转化。
//注释代码体现了异步与同步的执行
// console.log(1);
// let p = new Promise(resolve => {
// console.log(2);
// resolve();
// console.log(3);
// });
// console.log(4);
// p.then(() => {
// console.log(5);
// });
// console.log(6);
// 把同步的任务转成异步任务
function createAsyncTask(syncTask) {
return Promise.resolve(syncTask).then(syncTask => syncTask());
}
createAsyncTask(() => {
console.log('我变成了异步任务!!!');
return 1 + 1;
}).then(res => {
console.log(res);
});
console.log('我是同步任务!');