Promise 指一种抽象的异步处理工作,其核心概念为 “确保在一件事完成之后,再做另一件事”。
Promise 规范的内容不多,简单描述如下:
var promise = new Promise(function (resolve, reject) {
/*
做一些事情 */ if (true) {
resolve('一切正常');
} else {
reject(Error('处理失败'));
}
});
Promise 类的构造函数有一个参数,参数作为回调函数。该参数又有两个参数,参数值为两个回调函数。
跟传统的 throw 一样,在调用 reject 时使用一个 Error 对象的这种方法只是习惯,而非必须。使用 Error 对象的好处就是他可以捕捉到一个错误堆栈,从而使调试工具变得更加有利。
promise 有一个 then() 方法(这字眼好像在哪见过...)...:
/* 只指定成功 */ promise.then(function (result) {
location.hash += result;
});
/_ 只指定失败_/;
promise.then(function (error) {
location.hash += err;
});
then 是可以链式使用,连续运行附加的异步操作:
function CreateXMLHTTP() {
if (window.ActiveXObject) {
var objXmlHttp = new ActiveXObject('Microsoft.XMLHTTP');
} else {
if (window.XMLHttpRequest) {
var objXmlHttp = new XMLHttpRequest();
} else {
alert('不能够初始化 XMLHTTP 对象!');
}
}
return objXmlHttp;
}
function read() {
var fileName = 'user.json';
var promise = new Promise(function (resolve, reject) {
var objXmlHttp = CreateXMLHTTP();
objXmlHttp.open('GET', fileName, true);
objXmlHttp.onreadystatechange = function () {
if (objXmlHttp.readyState == 4) {
if (objXmlHttp.status == 200) {
resolve(objXmlHttp.responseText);
} else reject();
}
};
objXmlHttp.send();
});
promise
.then(
function (response) {
return JSON.parse(response);
},
function () {
alert('读取失败');
},
)
.then(function (obj) {
document.getElementById('user').value = obj.user;
document.getElementById('password').value = obj.password;
});
}
通过链式操作,调用 promise 对象的 then 方法可以实现列队的异步操作。当一个 then 方法的回调函数参数返回时,如果是一个值,下一个 then 则被立即调用,并且返回该值。然而,如果 then() 方法的回调参数返回一个 promise 对象,下一个 then() 方法将会对其等待,直到这个 promise 函数参数的处理结果确定后才被调用:
function CreateXMLHTTP() {
if (window.ActiveXObject) {
var objXmlHttp = new ActiveXObject('Microsoft.XMLHTTP');
} else {
if (window.XMLHttpRequest) {
var objXmlHttp = new XMLHttpRequest();
} else {
alert('不能够初始化 XMLHTTP 对象!');
}
}
return objXmlHttp;
}
function getData(fileName) {
return new Promise(function (resolve, reject) {
var objXmlHttp = CreateXMLHTTP();
objXmlHttp.open('GET', fileName, true);
objXmlHttp.onreadystatechange = function () {
if (objXmlHttp.readyState == 4) {
if (objXmlHttp.status == 200) {
allData += objXmlHttp.responseText + '';
resolve();
} else {
alert('读取文件失败');
}
}
};
objXmlHttp.send();
});
}
function read() {
allData = '';
getData('1.txt')
.then(function () {
return getData('2.txt');
})
.then(function () {
return;
getData('3.txt');
})
.then(function () {
var result = document.getElementById('result');
result.innerHTML = allData;
});
}
可以使用 catch() 来捕捉 Promise 构造函数的参数值中抛出的异常。
针对 then(func1,func2) 来说,只会出发其中一个回调函数,而 then(func1).catch(func2) 来说,两个都会被调用。
promise 对象的否定回调函数可以通过 promise.reject() 方法显式调用,也可以被 Promise 构造函数的参数值回调函数中抛出的异常错误隐式调用。
在使用 promise 对象序列时,需要用到 Promise 类的静态方法 resole() 。该方法最多可以拥有一个参数,当参数值为 promise 时, resolve() 方法传入的 promise 对象复制一个新的 promise 对象;如果传入其他的任何值, resolve() 方法将创建一个以这个值为肯定结果的 promise 对象;如果不指定参数值,将创建一个以 undefined 为肯结果的 promise 对象。
Promise 类存在一个最多可使用一个参数的 resolve() 静态方法,该方法创建一个 promise() 对象,该对象使用传入参数作为否定结果。如果不指定参数值,则创建一个 undefined 为否定的 promise 对象:
var result = document.getElementById('result');
var file = document.getElementById('file');
var allData = '';
function getData(file) {
return new Promise(function (resolve, reject) {
var reader = new FileReader(); // 将文件以文本形式进行读入页面
reader.readAsText(file);
reader.onload = function (f) {
allData += this.result + '';
resolve();
};
reader.onerror = function () {
reject();
};
});
}
function get(file) {
return getData(file).catch(function (err) {
alert('读取文件失败');
throw err;
});
}
function getSequence() {
var files = [];
for (var i = 0; i < document.getElementById('file').files.length; i++) {
files.push(document.getElementById('file').files[i]);
}
var sequence = Promise.resolve();
files.forEach(function (file) {
sequence = sequence.then(function () {
return get(file);
});
});
return sequence;
} // 将文件以文本形式进行读入页面
function read() {
Promise.resolve()
.then(function () {
return getSequence();
})
.then(function () {
var result = document.getElementById('result');
result.innerHTML = allData;
})
.catch(function () {
console.log('读取文件发生错误');
});
}
promise 的 all() 方法可以实现多个异步处理:
Promise.all(arrayOfPromise).then(function (arrayOfRequests) {
/_ some code_/;
});
function CreateXMLHTTP() {
if (window.ActiveXObject) {
var objXmlHttp = new ActiveXObject('Microsoft.XMLHTTP');
} else {
if (window.XMLHttpRequest) {
var objXmlHttp = new XMLHttpRequest();
} else {
alert("Can't intialize XMLHTTP object !");
}
}
return objXmlHttp;
}
function getData(fileName) {
return new Promise(function (resolve, reject) {
var objXmlHttp = CreateXMLHTTP();
objXmlHttp.open('GET', fileName, true);
objXmlHttp.onreadystatechange = function () {
if (objXmlHttp.readyState == 4) {
if (objXmlHttp.status == 200) {
resolve(objXmlHttp.responseText);
} else {
reject();
}
}
};
objXmlHttp.send();
});
}
function read() {
Promise.all([getData('1.txt'), getData('2.txt'), getData('3.txt')]).then(
function (responses) {
var result = document.getElementById('result');
responses.forEach(function (response) {
result.innerHTML += response + '';
});
},
function () {
alert('读取文件失败');
},
);
}
在 getData 函数中创建返回的 Promise 对象。如果其中有任何一个对象返回否定的结果,那么都将触发 error 。
HTML 5 还提供了一个 Promise.race() 方法,该方法同样以一个 Promise 对象数组作为参数,但是数组中任何元素返回肯定结果时, Promise.race() 方法立即返回 肯定结果;或全部返回否定结果时,立即返回否定结果。