完成一个异步操作通常分为四步:1.声明注册回调函数的接口。2.声明回调函数。3.注册回调函数。4.调用回调函数。
比如一个网络库,提供了一个registerConnectedCB(const ConnectedCB& cb)
的接口(声明了注册接口和回调函数),调用它往网络库中注册了一个连接成功时的回调函数。
这个回调函数的声明形式需要根据实际网络的库的要求和业务的需求进行设计,比如是应该有一个参数还是多个参数,是否应该有一个错误码标识等,这属于编码规范的范畴,这种设计在一些语言中并不被约束,比如在C++中就是如此。
但是在JS中就设计为语言层面的约束了,它通过Promise来统一异步操作的编码流程,规范代码风格。
这篇文章描述了我所认为的Promise几个要点。
Promise对象
Promise对象就像它的命名一样的行为,许诺,代表了一种未来的状态,现在对未来的许诺。MDN上的解释
A
<font style="color:rgb(27, 27, 27);">Promise</font>
is a proxy for a value not necessarily known when the promise is created.
<font style="color:rgb(27, 27, 27);">Promise</font>
代表了当前不需要知道结果的一种操作。启动一个操作,不需要马上知道结果(立即返回),结果会在稍后告知,这是一种异步流程。
**Promise**
对象就代表了这类异步流程。
使用Promise对象
- 通过
Promise
构造函数创建一个Promise对象
通过Promise
构造函数,产生一个promise对象,需要传入一个函数,这个函数就是实际的业务函数,promise对象包装的业务函数,都是异步处理的,所以需要一种方式知道执行的结果。
使用它的then
注册结果处理函数,如下代码:
var promise = new Promise(function (resolve){
console.log("inner promise"); // 1
resolve(42);
});
promise.then(function(value){
console.log(value); // 3
});
console.log("outer promise"); // 2
- 通过
Promise
的resolve
方法返回一个Promise对象
Promise.resolve
方法返回一个Promise
对象,这个对象会立刻进入resolve状态,其.then
中的成功回调会被执行。
Promise.resolve(42).then(function(value){
console.log(value);
})
等于下面的代码:
let p = new Promise(function(resolve){
resolve(42);
});
p.then(function(value){
console.log(value);
});
then
方法
Promise
的then()
方法最多接受两个参数;第一个参数是Promise
成功时的回调函数,第二个参数是Promise
失败时的回调函数。每个**then()**
返回一个新生成的**Promise**
对象,这个对象可被用于链式调用。
这个then
的返回值分几种情况:
- 回调中无返回值,它将返回一个fulfilled状态
Promise
对象,undefined
作为值。
let p = Promise.resolve(1);
p.then(function(res){
console.log(res); //输出 1
}).then(function(res1){
console.log(res1); //输出 undefined
})
- 回调中有返回值,返回值不是一个
Promise
对象,则将返回一个为fulfiled状态的promise对象,返回值作为值。
const p1 = Promise.resolve(1);
p1.then((value) => {
console.log(value); //输出 1
return value + 1;
}).then((value) => {
console.log(value); //输出 2
});
- 回调函数返回的是一个
Promise
对象,下一个then
中的回调等待这个promise对象执行状态改变后才会执行,要么是fulfilled,要么是rejected。
let p2 = Promise.resolve();
p2.then(()=>{
//返回一个状态为pending的promise,setTimeout执行后,状态标为fulfilled
return new Promise((resolve, reject)=>{
setTimeout(()=>{
console.log(1);
resolve();
},2000);
});
}).then(()=>{
console.log(2);//两秒后输出2
});
链式调用
不管注册到then
方法中回调返回怎么写,最终都会包装成一个Promise对象,所以再调用这个对象的then
就形成了链式调用。
最简单的链式调用就是在回调方法中不返回任何值,后续的回调方法也不需要前面回调的返回值。
function taskA() {
console.log("Task A");
return
}
function taskB() {
console.log("Task B");
}
function onRejected(error) {
console.log("Catch Error: A or B", error);
}
function finalTask() {
console.log("Final Task");
}
var promise = Promise.resolve();
promise
.then(taskA)
.then(taskB)
.catch(onRejected)
.then(finalTask);
taskA
–>taskB
–>finalTask
依次执行,如果在任意函数抛出一个异常,将会走catch
方法注册的回调,执行链会终止。
有返回值
function TaskA() {
return 1;
}
function addTwo(value) {
return value + 2;
}
function addThree(value) {
value += 3;
console.log("final value:"+value);
}
Promise.resolve().then(TaskA).then(addTwo).then(addThree);
上面两种形式都将被包装成fulfilled状态的Promise对象返回。
返回一个Pending的Promise对象,即还需要等上一次异步业务执行的结果确定Promise的状态,如下代码,这个也是一个更具意义的调用链示意代码
function ajax(url,sucessCb,failedCb) {
//1.创建XMLHttpRequest对象
var xmlHttp = new XMLHttpRequest();
//2.发送请求
xmlHttp.open('GET',url,true);
xmlHttp.send();
//3.服务端响应
xmlHttp.onreadystatechange = function() {
if (xmlHttp.readyState === 4 && xmlHttp.status === 200) {
var obj = JSON.parse(xmlHttp.response);
sucessCb && sucessCb(obj);
} else if (xmlHttp.readyState === 4 && xmlHttp.status === 404) {
failedCb && failedCb(xmlHttp.statusText);
}
}
}
function getPromise(url) {
return new Promise((reslove,reject)=> {
ajax(url,reslove,reject);
})
}
getPromise('static/a.json').then(res=>{
console.log(res);
return getPromise('static/b.json');
}).then(res=>{
console.log(res);
return getPromise('static/c.json');
},err=>{
console.log(err);
return getPromise('static/d.json');
}).then(res=>{
console.log(res);
})
顺序请求网络内容,a.json->b.json->c.json->d.json,每个then
中的回调都返回一个pending的Promise对象。
参考资料: