渊源:
前段时间在分析一款JS IM产品.由于对方使用了混淆.我一度分析到了Promise库中.
Promise是一个熟悉且陌生的单词,今天把它搞清楚.
let pro = new Promise()
Promise的优势在于,可以在then方法中继续写Promise对象并返回,然后继续调用then来进行回调操作。
在then方法中,你也可以直接return数据而不是Promise对象,在后面的then中就可以接收到数据了
先说清楚 我们这里指的Promise 是ES6原生的.而不是 jQuery库自己实现的Promise.
本小节举第一个例子:
1s后加载一张图片,再加载另一张图片
看一下不用Promise
的传统写法:
setTimeout(() => {
//加载第一张图片
let image1 = new Image();
image1.src = "https://static2.cnodejs.org/public/images/cnodejs_light.svg"
//加载成功调用这里
image1.onload = () => {
//将图片到界面并显示出来
console.log("第一张图片加载成功")
document.body.appendChild(image1);
setTimeout(() => {
//加载第二张图片
let image2 = new Image();
image2.src = "https://profile.csdnimg.cn/B/1/4/2_dalerkd"
image2.onload = () => {
console.log("第二张图片加载成功")
document.body.appendChild(image2);
}
},1000)
}
},1000)
这种顺序性任务 显然变成了 越来越长的东东。。。。真心难受.
如果连续20个相互依赖的步骤的任务呢?
这种情况 被 JS人 称为 回调地狱
.
其实我们工作中很多代码都是这种 有依赖性的
逻辑.eg:
检查密码是否为空->使用加密算法处理密码->发送给服务器并等待成功
是时候用原生Promise来做了:
以上代码可以写为:
function load_second_image(){
//加载第二张图片
let promise = new Promise((res,rej)=>{
let image2 = new Image();
image2.src = "https://profile.csdnimg.cn/B/1/4/2_dalerkd"
image2.onload = () => {
res('第二张图片加载成功')
document.body.appendChild(image2);
}
setTimeout(()=>{
rej('5秒内没有加载成功第2张图片,终止后续任务')
},5000)
})
return promise;
}
function load_first_image(){
let promise = new Promise((res,rej)=>{//我写了简称,毕竟只是函数而已嘛
let image1 = new Image();
image1.src = "https://static2.cnodejs.org/public/images/cnodejs_light.svg"
//加载成功调用这里
image1.onload = () =>{
res('第一张图片加载成功')
document.body.appendChild(image1);
}
setTimeout(()=>{
rej('5秒内没有加载成功第1张图片,终止后续任务')
},5000)
})
return promise;
}
load_first_image().then((value)=>{
console.log(value);
return load_second_image();
})
.then((value)=>{
console.log(value)
})
.catch(err=>{
console.log("失败:",err)
})
我们看到 可以在 .then
中 直接返回 Promise.resolve
或者 Promise.reject
来立马决定成功或者失败.
也可以 返回new Promise
.(例中就是这样,好处是不立马决定是成功还是失败)
还可以 直接 return
数据而不是Promise
对象.eg: return '成功完成.'
then返回什么?
var promise = new Promise((resolve, reject) => {
// 异步处理
// 处理结束后,调用 resolve 或 reject
// 成功时就调用 resolve
// 失败时就调用 reject
});
表面上是简化了 层层回调. 实质上,Promise的精髓是 “状态”.
用 维护状态 和 传递状态 的方式来使得回调函数能够及时调用.
promise().then().then().catch()
示例:
start()
.then(data => {
// promise start
console.log('result of start: ', data);
return Promise.resolve(1); // p1
)
.then(data => {
// promise p1
console.log('result of p1: ', data);
return Promise.reject({
notRealPromiseException: true,
}); // p2
})
.then(data => {
// promise p2
console.log('result of p2: ', data);
return Promise.resolve(3); // p3
})
.catch(ex => {
console.log('ex: ', ex);
if (ex.notRealPromiseException) {
// 一切正常,只是通过 catch 方法来中止 promise chain
// 也就是中止 promise p2 的执行
return true;
}
// 真正发生异常
return false;
});
start(){
let p = new Promise(function(){
setTimeout(()=>{return Promise.resolve('成功')})
}
)
return p;
}
.then(data=>{
return Promise.resolve('成功');
}
)
.then(data=>{
return Promise.reject('失败');//失败
}
)
.catch(err=>{
console.log(err);
}
)
Promise的.all
操作 传入 函数数组,能并发执行它们.
它之后的then
会等大家都完成后才将 大家的参数全部拼成一个数组来返回给then.
类似.all
只是,每完成一个Promise就立马用返回的参数调用then.
所以.then的参数不是数组在这里.
一种用法是:
将工作函数放在一个Promise中,将超时函数放在一个Promise中.
超时函数 在指定时间后 负责设置 reject.
由于设置成某个状态后,就不能改变了,所以就能在超时后直接设置状态.
catch 函数是用于处理失败情况的.
//请求某个图片资源
function requestImg(){
var p = new Promise(function(resolve, reject){
var img = new Image();
img.onload = function(){
resolve(img);
}
img.src = 'xxxxxx';
});
return p;
}
//延时函数,用于给请求计时
function timeout(){
var p = new Promise(function(resolve, reject){
setTimeout(function(){
reject('图片请求超时');
}, 5000);
});
return p;
}
Promise
.race([requestImg(), timeout()])
.then(function(results){
console.log(results);
})
.catch(function(reason){
console.log(reason);
});
文章主要参考:
大白话讲解Promise(一)
https://www.cnblogs.com/lvdabao/p/es6-promise-1.html
Promise 的链式调用与中止
https://cnodejs.org/topic/58385d4927d001d606ac197d