1. 概述
javascript 是单线程语言 (单线程 / 多线程, 阻塞 / 非阻塞, 同步, 异步) 参考 此文章 , 所有的任务都是按顺序执行的, 但是对于很耗时的操作采用异步的方式 (前一个任务结束的时候, 不是执行下一个任务, 而是执行回调函数, 后一个任务则是不等前一个任务结束就执行) 参考此文章, js 中异步编程的四种方法: 回调函数, 事件监听, 发布 / 订阅, Promise 对象, 这里讨论 promise 的用法, promise 是 es6 增加的内容, 使得在异步编程中不用过多的嵌套回调函数, 让异步操作变得更加简单
2.Promise 介绍
2.1 Promise 构造函数
Promise 构造函数接收一个函数作为参数, 此函数又有两个参数分别为 resolve 和 reject, 这两个参数也是函数
const promise = new Promise(function(resolve, reject) {
// 异步操作的代码
if (success) {
return resolve(data); // data 为异步操作成功返回的数据
} else {
return reject(error); //data 为失败时返回的数据
}
})
2.2 resolve 和 reject
promise 对象类似于一个容器(也可以说是一个状态机), 里面包含着异步操作, 异步操作会有两种结果: 成功或失败. 当异步操作成功就会将 pending(执行中状态) 转为 fulfilled(成功状态) 同时触发 resolve 函数, 用来将操作成功后得到的结果传递出去; 当异步操作失败就会将 pending(执行中状态) 转为 reject(拒绝状态) 同时触发 reject 函数, 用来将操作失败后报出的错误传递出去
const promise = new Promise(function(resolve, reject) {
// 异步操作的代码
if (success) {
return resolve(data); // data 为异步操作成功返回的数据
} else {
return reject(error); //data 为失败时返回的数据
}
})
我们来看下面代码:
function fn1() {
return new Promise(function(resolve, reject){
setTimeout(() => {
console.log('fn1')
},1000)
})
}
function fn2() {
return new Promise(function(resolve, reject){
setTimeout(() => {
console.log('fn2')
},2000)
})
}
fn1().then(fn2)
输出为:
fn1
fn2 函数不执行, 这个时候我们需要调用 resolve 函数以便执行 then() 方法中的回调函数 fn2
function fn1() {
return new Promise(function(resolve, reject) {
setTimeout(() = >{
console.log('fn1') resolve('fn1')
},
1000)
})
}
输出为:
fn1fn2
如果我们在 fn1 中调用 reject 函数时会出现什么情况呢?
function fn1() {
return new Promise(function(resolve, reject){
setTimeout(() => {
console.log('fn1')
// resolve('fn1')
reject('错误'+'fn1')
},1000)
})
}
fn1().then(fn2).then((data) => {
console.log(data)
})
输出为:
提示错误未捕获, 所以需要在 then() 方法中添加第二个回到函数来处理出错信息
fn1().then(fn2).then((data) => {
console.log(data)
}, (err) => {
console.log(err)
})
输出为:
2.3 then()方法
当 promise 实例生成以后, 后面跟 then 方法, 其的第一个回调函数来处理 resolve 函数传递的数据, 第二个回调函数来处理 reject 函数传递的数据, 以上的流程用代码展示如下
promise.then(function(data) {
// 拿到返回的数据之后做一些处理
console.log(data)
},
function(error) {
// 返回失败时做一些处理
console.log(error)
})
2.3.1 then() 的返回值
then()方式是 Promise 实例的方法, 此 then 方法定义在原型对象 (Promise.prototype) 上, then() 方法的返回值是一个新的 Promise 实例 (不是原来那个 Promise, 原来那个 Promise 已经承诺过) 所以我们可以采用链式的写法
var p1 = new Promise((resolve, reject) = >{
setTimeout(() = >resolve('p1'), 10);
});
p1.then(ret = >{
console.log(ret);
return 'then1';
}).then(ret = >{
console.log(ret);
return 'then2';
}).then(ret = >{
console.log(ret);
});
执行顺序:
p1>then1>then2
从第二个 then() 方法开始, 它们的 resolve 中的参数就是前一个 then() 中的 resolve 的 return 语句的返回值 采用链式的 then, 可以指定一组按照次序调用的回调函数. 这时, 前一个回调函数, 有可能返回的还是一个 Promise 对象(即有异步操作), 这时后一个回调函数, 就会等待该 Promise 对象的状态发生变化, 才会被调用
getJSON("/post/1.json").then(function(post) {
return getJSON(post.commentURL);}).then(function funcA(comments) {
console.log("resolved:", comments);}, function funcB(err){
console.log("rejected:", err);})
上面代码中, 第一个 then 方法指定的回调函数, 返回的是另一个 Promise 对象. 这时, 第二个 then 方法指定的回调函数, 就会等待这个新的 Promise 对象状态发生变化. 如果变为 resolved, 就调用 funcA, 如果状态变为 rejected, 就调用 funcB. 如果采用箭头函数, 上面的代码可以写得更简洁
getJSON("/post/1.json").then(
post => getJSON(post.commentURL)).then(
comments => console.log("resolved:", comments),
err => console.log("rejected:", err));
promise 链式写法如下:
function fn1() {
return new Promise(function(resolve, reject) {
setTimeout(() = >{
console.log('fn1') resolve('fn1')
},
1000)
})
}
function fn2() {
return new Promise(function(resolve, reject) {
setTimeout(() = >{
console.log('fn2') resolve('fn2')
},
2000)
})
}
function fn3() {
return new Promise(function(resolve, reject) {
setTimeout(() = >{
console.log('fn3') resolve('fn3')
},
3000)
})
}
function fn4() {
return new Promise(function(resolve, reject) {
setTimeout(() = >{
console.log('fn4') resolve('fn4')
},
4000)
})
}
fn1().then(fn2).then(fn3).then((data) = >{
console.log(data)
},
(err) = >{
console.log(err)
})
依次输出为:
fn1>fn2>fn3
同时我们还可以更改执行的先后顺序
function fn1() {
return new Promise(function(resolve, reject) {
setTimeout(() = >{
console.log('fn1') reject(false)
},
1000)
})
}
function fn2() {
return new Promise(function(resolve, reject) {
setTimeout(() = >{
console.log('fn2') resolve('fn2')
},
2000)
})
}
function fn3() {
return new Promise(function(resolve, reject) {
setTimeout(() = >{
console.log('fn3') resolve('fn3')
},
3000)
})
}
function fn4() {
return new Promise(function(resolve, reject) {
setTimeout(() = >{
console.log('fn4') resolve('fn4')
},
4000)
})
}
fn1().then(fn2).then(fn3).then((data) = >{
console.log(data)
},
(err) = >{
if (err == false) {
fn3().then(fn4)
}
})
输出为:
fn1>fn3>fn4
2.4 catch()方法
我们在开发时倾向于用 catch()方法来处理异常, 而不是在 then() 方法里写入第二个回调函数, 这种写法类似于. then(null, rejection)
promise.then(function(data) {
// 拿到返回的数据之后做一些处理
console.log(data)
}).
catch(function(error) {
// 返回失败时做一些处理
console.log(error)
}
为什么要采用这么 catch()方法呢? 我们来看一个例子:
const promise = new Promise((resolve,reject) => {
console.log(1)
resolve('成功')
})
promise
.then((data) => {
console.log(data)
console.log(a)
}, (err) => {
console.log(err)
})
输出为:
1 成功
但是没有捕捉到回调函数里 a 未定义 这个时候我们来该写以上代码
1 成功
ReferenceError: a is not defined
Promise 对象的 Error 对象具有冒泡性质, 会一直向后传递, 直到被捕获为止. 也就是说, 错误总是会被下一个 catch 语句捕获
var p = new Promise( (resolve, reject) => {
setTimeout(() => resolve('p1'), 10);
});
p.then( ret => {
console.log(ret);
throw new Error('then1');
return 'then1';
}).then( ret => {
console.log(ret);
throw new Error('then2');
return 'then2';
}).catch( err => {
// 可以捕抓到前面的出现的错误.
console.log(err.toString());
});
输出为:
p1Error: then1
2.4.1 catch()返回值
既然 catch() 是. then(null, rejection) 的别名, 那么 catch() 就会返回一个 Promise 对象, 因此在后面还可以接着调用 then 方法
var p = new Promise((resolve, reject) => {
resolve(x + 2);
});
p.then( () => {
console.log('nothing');
}).catch( err => {
console.log(err.toString());
return 'catch';
}).then( ret => {
console.log(ret);
});
输出为:
ReferenceError: x is not defined
catch
当出错时, catch 会先处理之前的错误, 然后通过 return 语句, 将值继续传递给后一个 then 方法中, 如果没有报错, 则跳过 catch, 示例如下:
var p = new Promise((resolve, reject) => {
resolve('p');
});
p.then( ret => {
console.log(ret);
return 'then1';
}).catch( err => {
console.log(err.toString());
return 'catch';
}).then( ret => {
console.log(ret);
});
3. promise 用法解析
3.1 用 Promise 实现 ajax 操作
const getJSON = function(url) {
const promise = new Promise(function(resolve, reject) {
const handler = function() {
if (this.readyState !== 4) {
return;
}
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
const client = new XMLHttpRequest();
client.open("GET", url);
client.onreadystatechange = handler;
client.responseType = "json";
client.setRequestHeader("Accept", "application/json");
client.send();
});
return promise;
};
getJSON("/posts.json").then(function(json) {
console.log('Contents:' + json);
},
function(error) {
console.error('出错了', error);
});
3.2 promise 和 ajax 方法结合
var http = {
get: function(url) {
var promise = new Promise(function(resolve, reject) {
$.ajax({
url: url,
method: 'get',
success: function(data) {
resolve(data);
},
error: function(xhr, statusText) {
reject(statusText);
}
});
});
return promise;
}
};
http.get('data.php').then(function(data) {
document.write(data);
},
function(err) {
document.write(err);
});
3.3 用 promise 实现异步加载图片的例子
function loadImageAsync(url) {
return new Promise(function(resolve, reject) {
const image = new Image();
image.onload = function() {
resolve(image);
};
image.onerror = function() {
reject(new Error('Could not load image at' + url));
};
image.src = url;
});
}
var loadImage1 = loadImageAsync(url);
loadImage1.then(function success() {
console.log("success");
}, function fail() {
console.log("fail");
});
3.4 promise 和 axios 方法结合
function fetch(url, params) {
return new Promise((resolve, reject) => {
axios.post(url, params)
.then(response => {
resolve(response.data);
}, error => {
reject(error);
})
.catch(error => {
reject(error)
})
})
}
function lineStatus(params) {
return fetch('/ProductionLine/GetStatus', params)
}
function statisticsData(params) {
return fetch('/ProductionLine/GetWeightStatistics', params)
}
lineStatus(this.lineId)
.then((result) => {
this.deviceStatus.run = result.TotleMoveMotor
this.deviceStatus.stop = result.TotleStopMotor
this.deviceStatus.lost = result.TotleLoseMotor
this.deviceStatus.alarm = result.TotleAlarmMotor
this.ProductionStatus = result.ProductionStatus
console.log(result)
})
.catch((error) => {
console.log('瓦特了...(;′⌒`)')
})
statisticsData(this.lineId)
.then((result) => {
this.outputData.totalOutput = result.MainConveyorModel.OutPut
this.outputData.instantWeight = result.MainConveyorModel.InstantWeight
this.outputData.runningTime = result.MainConveyorModel.RunningTime
this.outputData.motorLoad = result.MainConveyorModel.MotorLoad
// console.log(result)
})
.catch((error) => {
console.log('瓦特了...(;′⌒`)')
})
以上是 Promise 在开发中常见的用法, 参考了以下几篇文章, 特此感谢作者
Promise 对象 [阮一峰]
[es6 系列] 学习 Promise
阮一峰的 ES6---Promise 对象
对 Promise 中的 resolve,reject,catch 的理解
来源: https://juejin.im/post/5a730110f265da4e777f7792