标签:
讲述Promise之前,先向大家推荐一个不错的关于Promise的学习资料JavaScript Promise迷你书(中文版)
var promise = new Promise(function(resolve, reject) {
resolve("示例");
});
promise.then(function(value) {
console.log(value);
}, function(error) {
console.log(error);
});
Promise是抽象异步处理对象以及对其进行各种操作的组件。
getAsync("fileA.txt", function(error, result){
if(error){
// 取得失败时的处理
throw error;
}
// 取得成功时的处理
});
注意:Node.js等规定在JavaScript的回调函数的第一个参数为 Error 对象,这也是它的一个惯例。
var promise = new Promise(function(resolve, reject): {
// 异步处理
// 处理结束后、调用resolve(成功) 或 reject(失败)
});
promise.then(onFulfilled, onRejected)
(1)new Promise(function(resolve, reject):Promise构造器,返回一个promise对象
(2)resolve(成功):调用 onFulfilled
(3)reject(失败):调用 onRejected
补充:Pending:promise对象刚被创建后的初始化状态
(4)promise.then(onFulfilled, onRejected):pomise对象会在变为resolve或者reject的时候分别调用相应注册的回调函数
等价于(promise.then的语法糖):
promise.then(function(value) {
// onFulfilled 成功
}).catch(function(error){
// onRejected 失败
});
(5)promise.catch(function(error) {}):用来注册当promise对象状态变为Rejected时的回调函数;是 promise.then(undefined, onRejected);
方法的一个别名(语法糖)
注意:catch 是ECMAScript的 保留字 (Reserved Word)。基于ECMAScript 3实现的浏览器,会出现 identifier not found 这种语法错误;而在ECMAScript 5中保留字都属于 IdentifierName ,也可以作为属性名使用了。
为了回避ECMAScript保留字带来的问题,可以使用 promise["catch"](function(error) {})
如果需要支持IE8及以下版本的浏览器的话,那么一定要将这个 catch 问题牢记在心中!!!
function getURL(url) {
return 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();
});
}
// 运行示例
getURL("http://httpbin.org/get").then(function onFulfilled(value) {
console.log(value);
}, function onRejected(error) {
console.log(error);
});
示例(1):
Promise.resolve("ligang").then(function(value) {
console.log(value);
});
可以认为是下述代码的语法糖:
new Promise(function(resolve) {
resolve("ligang");
}).then(function(value) {
console.log(value);
});
示例(2):
Promise.reject("system error").catch(function(error) {
console.error(error);
});
可以认为是下述代码的语法糖:
new Promise(function(resolve, reject) {
reject("system error");
}).then(null, function(error) {
console.error(error);
});
var promise = new Promise(function(resolve, reject){
console.log(1);
resolve(2);
});
promise.then(function(value) { // .then 中指定的方法调用是异步进行的
console.log(value);
});
console.log(3);
// 运行结果: 1 -> 3 -> 2
这个问题的本质是接收回调函数的函数,会根据具体的执行情况,可以选择是以同步还是异步的方式对回调函数进行调用。在开发中经常出现!!
function onReady(fn) {
var readyState = document.readyState;
if(readyState === ‘interactive‘ || readyState === ‘complete‘) {
fn();
}else {
window.addEventListener(‘DOMContentLoaded‘, fn);
}
}
onReady(function() {
console.log(1)
});
console.log(2);
运行结果:
出现上述两种情况,往往会导致程序不能按预期执行。
(1)异步调用方式
function onReady(fn) {
var readyState = document.readyState;
if(readyState === ‘interactive‘ || readyState === ‘complete‘) {
setTimeout(fn, 0);
}else {
window.addEventListener(‘DOMContentLoaded‘, fn);
}
}
onReady(function() {
console.log(1)
});
console.log(2);
// 运行结果统一输出:2 -> 1
(2)Promise重写
function onReadyPromise() {
return new Promise(function(resolve, reject) {
var readyState = document.readyState;
if(readyState === ‘interactive‘ || readyState === ‘complete‘) {
resolve();
}else {
window.addEventListener(‘DOMContentLoaded‘, resolve);
}
});
}
onReadyPromise().then(function(){
console.log(1);
}).catch(function(){
console.error("error");
});
console.log(2);
// 运行结果统一输出:2 -> 1
总结:
Promise里可以将任意个方法连在一起作为一个方法链
function taskA(value) {
console.log("Task A");
return value = value + 1;
}
function taskB(value) {
console.log("Tash B");
return value = value * 2;
}
function onRejected(error) {
console.log("Catch Error:A or B", error);
}
function finalTask(value) {
console.log("Final Tash", value);
}
var promise = Promise.resolve(1);
// taskA、taskB的错误,都会被onRejected捕获
promise.then(taskA)
.then(taskB)
.catch(onRejected)
.then(finalTask);
var promise = new Promise(function(resolve) {
resolve(100);
});
var thenPromise = promise.then(function(value) {
console.log(value);
});
var catchPromise = thenPromise.catch(function(error) {
console.error(error);
});
console.log(promise === thenPromise); // false
console.log(thenPromise === catchPromise); // false
注意:在对Promise进行扩展的时候需要牢牢记住这一点,否则稍不留神就有可能对错误的promise对象进行了处理
// 错误使用
function badAsyncCall() {
var promise = Promise.resolve();
promise.then(function() {
// 任意处理
return newVar;
});
return promise;
}
// 正确使用,返回新的promise
function anAsyncCall() {
var promise = Promise.resolve();
return promise.then(function() {
// 任意处理
return newVar;
});
}
数组里的所有promise对象全部变为resolve或reject状态的时候,它才会去调用 .then 方法
function timerPromisefy(delay) {
return new Promise(function(resolve, reject) {
setTimeout(function(){
resolve(delay);
}, delay);
});
}
var startTime = Date.now();
Promise.all([
timerPromisefy(8),
timerPromisefy(16),
timerPromisefy(32)
]).then(function(result) {
console.log(Date.now() - startTime); // 约32ms
console.log(result); // [8, 16, 32]
});
说明:Promise.all([])中的方法会同时开始执行(并行),而每个promise的结果和传递给Promise.all的promise数组的顺序是一致的。
数组里只要有一个promise对象变为resolve或reject状态的时候,就会去调用 .then 方法
function timerPromisefy(delay) {
return new Promise(function(resolve, reject) {
setTimeout(function(){
resolve(delay);
}, delay);
});
}
var startTime = Date.now();
Promise.race([
timerPromisefy(8),
timerPromisefy(16),
timerPromisefy(32)
]).then(function(result) {
console.log(Date.now() - startTime); // 约8ms
console.log(result); // 8
});
类Promise对象。拥有名为.then方法的对象。 jQuery.ajax(),它的返回值就是thenable对象。
Promise.resolve方法可以将Thenable对象转换为Promise对象。
var promise = Promise.resolve($.ajax(‘/json/comment.json‘)); // Thenable对象 => promise对象
promise.then(function(value){
console.log(value);
});
在Promise类库之间进行相互转换是使用Thenable的最佳场景
例如:将ES6的promise对象转换为Q的promise的对象
var Q = require("Q");
// 这是一个ES6的promise对象
var promise = new Promise(function(resolve){
resolve(1);
});
// 变换为Q promise对象
Q(promise).then(function(value){
console.log(value);
}).finally(function(){
console.log("finally");
});
function copyOwnFrom(target, source) {
Object.getOwnPropertyNames(source).forEach(function (propName) {
Object.defineProperty(target, propName, Object.getOwnPropertyDescriptor(source, propName));
});
return target;
}
function TimeoutError() {
var superInstance = Error.apply(null, arguments);
copyOwnFrom(this, superInstance);
}
TimeoutError.prototype = Object.create(Error.prototype);
TimeoutError.prototype.constructor = TimeoutError;
function delayPromise(ms) {
return new Promise(function (resolve) {
setTimeout(resolve, ms);
});
}
function timeoutPromise(promise, ms) {
var timeout = delayPromise(ms).then(function () {
return Promise.reject(new TimeoutError(‘Operation timed out after ‘ + ms + ‘ ms‘));
});
return Promise.race([promise, timeout]);
}
function cancelableXHR(URL) {
var req = new XMLHttpRequest();
var promise = new Promise(function (resolve, reject) {
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.onabort = function () {
reject(new Error(‘abort this request‘));
};
req.send();
});
var abort = function () {
// 如果request还没有结束的话就执行abort
// https://developer.mozilla.org/en/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest
if (req.readyState !== XMLHttpRequest.UNSENT) {
req.abort();
}
};
return {
promise: promise,
abort: abort
};
}
var object = cancelableXHR(‘http://httpbin.org/get‘);
// main
timeoutPromise(object.promise, 1000).then(function (contents) {
console.log(‘Contents‘, contents);
}).catch(function (error) {
if (error instanceof TimeoutError) {
object.abort();
return console.log(error);
}
console.log(‘XHR Error :‘, error);
});
Deferred和Promise不同,它没有共通的规范,每个Library都是根据自己的喜好来实现的。比如 jQuery.Deferred 和 JSDeferred 等
Deferred和Promise并不是处于竞争的关系,而是Deferred内涵了Promise。
function Deferred() {
this.promise = new Promise(function(resolve, reject) {
this._resolve = resolve;
this._reject = reject;
}.bind(this));
}
Deferred.prototype.resolve = function(value) {
this._resolve.call(this.promise, value);
};
Deferred.prototype.reject = function(error) {
this._reject.call(this.promise, error);
};
function getURL(url) {
var deferred = new Deferred();
var req = new XMLHttpRequest();
req.open(‘GET‘, url, true);
req.onload = function() {
if(req.status === 200) {
deferred.resolve(req.responseText);
}else {
deferred.reject(new Error(req.statusText));
}
}
req.onerror = function() {
deferred.reject(new Error(req.statusText));
}
req.send();
return deferred.promise;
}
getURL("http://httpbin.org/get").then(function(value) {
console.log(value);
}).catch(function(error) {
console.error(error);
});
区别在于上述图中的“特权方法”。如上述,对比上述两个版本的 getURL:
1. Deferred 的话不需要将代码用Promise括起来,由于没有被嵌套在函数中,可以减少一层缩进
2. Deferred 没有Promise里的错误处理逻辑
3. Promise一般都会在构造函数中编写主要处理逻辑,对 resolve、reject 方法的调用时机也基本是很确定的
new Promise(function (resolve, reject){
// 在这里进行promise对象的状态确定
});
4. 而使用Deferred的话,并不需要将处理逻辑写成一大块代码,只需要先创建deferred对象,可以在任何时机对
resolve、reject 方法进行调用。
var deferred = new Deferred(); // 可以在随意的时机对
`resolve`、`reject` 方法进行调用
Promise代表了一个对象,这个对象的状态现在还不确定,但是未来一个时间点它的状态要么变为正常值(FulFilled),要么变为异常值(Rejected);而Deferred对象表示了一个处理还没有结束的这种事实,在它的处理结束的时候,可以通过Promise来取得处理结果。
标签:
原文地址:http://blog.csdn.net/ligang2585116/article/details/51417334