js中的异步事件处理
ES6诞生以前,异步编程的方法主要有以下几种:回调函数、事件监听、发布/订阅、Promise对象;ES6中引入了Generator函数;ES7中,async/await将异步编程带入了一个全新的阶段。
文章将对这些异步操作的使用和优缺点进行进一步的比较和分析。本文内容部分摘抄自《Mozilla Promise Reference》。
1.1 什么是Promise
Promise是抽象异步处理对象以及对其进行各种操作的组件。
// 使用了回调函数的异步处理
getAsync("fileA.txt", function(error, result) {
if(error) { // 取得失败时的处理
throw error;
}
// 成功时的处理
})
Node.js规定在javascript的回调函数中的第一个参数为Error
对象。
下面看个使用了Promise进行异步处理的例子:
var promise = getAsyncPromise("fileA.txt"); // 返回Promise对象
promise.then(function(result) {
// 成功时的处理
}).catch(function(error) {
// 失败时的处理
})
两者比较,回调函数方式可以自己定义回调函数的采纳数,而使用promise对象处理的时候,除了promise规定的then
和catch
以外的方法都是不可以使用的,必须严格遵守固定、统一的编程方式来写代码。
所以,promise的功能就是可以将复杂的异步处理轻松的进行模式化。
1.1.1 Promise的语法
Promise的使用大概有以下三种类型:
Constructor
Promise类似于XMLHttpRequest
, 从构造函数Promise
创建一个新的promise实例
var promise = new Promise(function(resolve, reject) {
// 处理结束后,调用resolve或reject
})
Instance Method
对通过new生成的promise对象,可以使用promise.then()
方法来调用resolve或reject的回调函数。
promise.then(onFulfilled, onRejected)
resolve成功 onFulfilled
会被调用
reject失败 onRejected
会被调用
promise.then
成功和失败时都可以使用。在只想对异常进行处理时,可以采用promise.then(undefined, onRejected)
这种方式,只指定reject时的回调函数。不过这种情况下,promise.catch(onRejected)
是个更好的选择。
promise.catch(onRejected);
Static Method
Promise包括一些静态方法如Promise.all()
,Promise.resolve()
等。
1.1.2 Promise的三种状态
状态 | 描述 |
---|---|
pending | 初始状态,既不是成功也不是失败,也就是promise实例刚被创建的初始状态。 |
fulfilled | resolve(成功),此时会调用onFulfilled |
rejected | reject(失败), 此时会调用onRejected |
1.1.3 Promise示例代码
function getURL(URL) {
var promise = new Promise(function (resolve, reject) {
var req = new XMLHttpRequest();
req.open('GET', URL, true);
req.onload = function () {
if(req.status == 200) {
resolve(req.responseText);
}
else{
reject(new Error(req.statusText));
}
};
req.onerror = function() {
reject(new Error(req.statusText));
};
req.send();
})
}
var URL = 'http://xxx.xx.txt';
getURL(URL).then(function onFulfilled(value) {
console.log(value);
}).catch(function onRejected(error) {
console.error(error);
})
1.2 Promise.prototype
Promise实例继承自Promise.prototype. 你可以通过构造器的原型对象在所有的Promise实例上添加属性或方法。
Promise.prototype.constructor
Promise.prototype.constructor
表示Promise的构造函数的原型,以便在其原型上放置then()
、catch()
、finally()
方法。
Promise.prototype.then(onFulfilled, onRejected)
为promise添加成功或者失败处理,同时返回一个新的包含解析过后的的值或者处理函数的promise,或者这个原始的未被处理的promise。
Promise.prototype.catch(onRejected)
为promise添加失败的回调处理,如果这个回调函数被调用了,则返回一个包含回调返回值的新的promise,如果这个promise没被处理,则返回原始的值。
Promise.prototype.finally(onFinally)
为promise添加处理,如果原始的promise对象已经被解析,则返回解析后的promise。无论promise是什么状态,这个处理函数都会被执行。
Promise的静态方法
Promise.resolve
一般情况下,我们会使用new Promise()
来创建promise对象,我们也可以使用Promise.resolve
或Promise.reject()
例如Promise.resolve(42)
,可以认为是以下代码的语法糖:
new Promise(resolve => {
resolve(42);
})
Promise.all(iterable)
当iterable参数中所有的promises都被resolve时,或参数中不包含promises的时候,Promise.all(iterable)
会返回一个单一的Promise。当iterable中有一个promise返回拒绝,all()方法会立即终止并返回第一个被reject的promise对象。
var promise1 = Promise.resolve(3);
var promise2 = 42;
var promise3 = new Promise(function(resolve, reject) {
setTimeout(resolve, 100, 'foo');
})
Promise.all([promise1, promise2, promise3]).then(function(values) {
console.log(values);// 3, 42, foo
})
Promise.race(iterable)
Promise.race(iterable)
方法返回iterable参数中最先被resolve或者reject的promise对象,包括返回值或者失败原因。
var promise1 = new Promise((resolve, reject) => {
setTimeout(resolve, 300, 'one');
});
var promise2 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'two');
});
Promise.race([promise1, promise2]).then(result => {
console.log(result);// expected 'two'
})