对象方法与实例方法的表示
实例方法:Promise#then
,表示为Promise的实例对象的then方法
对象方法:Promise.all
,表示为Promise类的静态方法
1. 什么是Promise
1.1 构造器
Copy var promise = new Promise((resolve, reject) {
// 异步处理
// 处理结束之后调用resolve或者reject
})
1.2 实例方法
promise.then(onFulfilled, onRejected)
当resolve(成功)时,onFulfilled会被调用, 当reject(失败)时,onRejected会被调用。
promise.then
在成功和失败时都可以调用,如果只想处理异常情况,可以使用 promise.then(undefined, onRejected)
,即只指定reject的回调函数。
不过这种情况下promise.catch(onRejected)
应该是更好的选择,即推荐使用.catch
来将 resolve
和 reject
处理分开来写,而不是使用 .then(onResolve,onReject)
Copy new Promise((resolve,reject) => {
return (2)
resolve(1);
}).then(res => {
console.log(res) // 不打印,promise一直是pending
})
new Promise((resolve,reject) => {
resolve(1);
return (2)
}).then(res => {
console.log(res) // 打印1, 与return的值无关
})
new Promise((resolve,reject) => {
reject(1);
return (2)
}).then(res => {
console.log(res)
},(ee) => {
console.log("@@",ee) // 打印 1,即错误由这里捕获
}).catch((err) => {
console.log(err) // 不打印
})
1.3 静态方法
Promise.all()
,Promise.resolve()
,Promise.reject()
等
1.4 promise的状态
promise的三种状态:Fulfilled
, Rejected
, Pending
.暂时没有查询其内部状态的方法。
promise对象的状态从 pending
转为 fulfilled
或者 rejected
之后,这个对象的状态就不会再发生任何变化,即 .then
后执行的函数之后被调用一次。
2. 实战 Promise
2.1 创建promise对象
一般情况下可以使用 new Promise()
来创建promise对象,同时还可以使用Promise.resolve()
和Promise.reject()
这两个方法。
Promise.resolve(42)
可以认为是以下代码的语法糖:
Copy new Promise(resolve => {
resolve(42)
})
即 Promise.resolve(value)
返回值也是一个 promise对象
,因此可以使用.then
进行调用。
Copy Promise.resolve(42).then(value => {
console.log(value); // 42
})
Promise.resolve
作为 new Promise()
的快捷方式,在进行promise对象的初始化
或者编写测试代码时都非常方便。
2.2 thenable
Promise.resolve
的另一个作用就是将 thenable(即,拥有.then方法)
的对象转换为 promise
对象。
这种将 thenable对象
转换为 promise对象
的机制要求 thenable对象
所拥有的 then
方法与 Promise
对象的 then
方法具有相同的功能和处理过程。
Copy Promise.resolve($.ajax('/json/comment.json')).then(value => {
console.log(value)
}).catch(err => {
console.log(err)
})
即转换为 promise
对象之后,就能直接使用 then
或者 catch
等方法了。
2.3 Promise.reject()
Promise.reject(error)
也是 new Promise()
方法的快捷方式。Promise.reject(new Error('error'))
是下面代码的语法糖:
Copy new Promise((resolve,reject) => {
reject('error')
})
// 将错误对象传递给onReject函数处理
Promise.reject('error').catch(function(error){
console.error(error);
});
2.4 Promise只能进行异步操作
Copy var promise = new Promise(function (resolve){
console.log("inner promise"); // 1
resolve(42); // 即使是立即resolve,也是异步的调用.then方法
});
promise.then(function(value){
console.log(value); // 3
});
console.log("outer promise"); // 2
2.5 try catch 不能捕获异步的error
Copy let a = null;
try {
a = setTimeout(() => {
throw new Error('123') // try catch 不能捕获异步的异常,这里无打印
}, 2000)
} catch (e){
console.log(e)
}
console.log(a)
2.6 方法链 Promise#then
Copy new Promise((resolve,reject) => {
resolve(1);
}).then(res => {
new Promise((resolve,reject) => { // 此时这段代码返回值为undefined
console.log('res from', res)
setTimeout(()=>{
console.log('timeout')
resolve(res+2)
}, 100)
})
}).then(res => {
console.log('last', res) // 则此时的res为undefined
}).catch(e => {
console.log('err',e);
})
new Promise((resolve,reject) => {
resolve(1);
}).then(res => {
return new Promise((resolve,reject) => { // 此时这段代码返回值为promise,即必须用return返回
console.log('res from', res)
setTimeout(()=>{
console.log('timeout')
resolve('i am promise')
}, 100)
})
}).then(res => {
console.log('last', res) // 则此时的res为 i am promise
}).catch(e => {
console.log('err',e);
})
2.7 异常处理
Copy function taskA() {
console.log("Task A");
}
function taskB() {
console.log("Task B");
}
function onRejected(error) {
console.log("Catch Error: A or B", error);
}
function finalTask() {
console.log("Final Task");
}
Promise.resolve()
.then(taskA)
.then(taskB)
.catch(onRejected)
.then(finalTask);
即当 taskA
或者 taskB
中出现异常(这种异常可以是 throw一个error
,或者返回一个 rejected状态的promise对象
),都会进入 onRejected
处理。
Copy function taskA() {
console.log("Task A");
new Promise((res,rej) => { // 这种方式虽然有reject的promise对象,但是没有用return返回,因此并不会进入catch
rej(123);
})
}
function taskB() {
console.log("Task B");
}
function onRejected(error) {
console.log("Catch Error: A or B", error);
}
function finalTask() {
console.log("Final Task");
}
Promise.resolve()
.then(taskA)
.then(taskB)
.catch(onRejected)
.then(finalTask);
由于在 onRejected
和 finalTask
两个任务后面没有 catch
处理,因此如果在这两个任务出现异常,将不会被捕获。
Copy function taskA() {
console.log("Task A");
return new Promise((res,rej) => { // 用return返回,则不会执行taskB,而是进入catch中
rej(123);
})
}
function taskB() {
console.log("Task B");
}
function onRejected(error) {
console.log("Catch Error: A or B", error);
}
function finalTask() {
console.log("Final Task");
}
Promise.resolve()
.then(taskA)
.then(taskB)
.catch(onRejected)
.then(finalTask);
2.8 Promise#then 参数传递
如果两个任务之间要进行值的传递,则需要在上一个任务中将值 return 出来
Copy function doubleUp(value) {
return value * 2;
}
function increment(value) {
// return的值会作为下一个任务的参数,return的值会被Promise.resolve()进行包装,即每次调用then都是返回一个promise对象
return value + 1;
}
function output(value) {
console.log(value);// => (1 + 1) * 2
}
var promise = Promise.resolve(1);
promise
.then(increment)
.then(doubleUp)
.then(output)
.catch(function(error){
// promise chain中出现异常的时候会被调用
console.error(error);
});
2.9 Promise#catch
Promise#catch
只是 Promise#then(undefined,onRejected)
方法的别名,即当 promise对象
状态变为 Rejected
时的回调。
2.10 then 和 catch 都是返回新的 promise 对象
Copy var aPromise = new Promise(function (resolve) {
resolve(100);
});
var thenPromise = aPromise.then(function (value) {
console.log(value);
});
var catchPromise = aPromise.catch(function (value) {
console.log(value);
});
console.log(aPromise !== thenPromise); // true
console.log(aPromise !== catchPromise); // true
2.11 Promise.all 与 Promise.race
Promise.all
: 当数组里所有的 promise对象
全部变为 resolve或reject状态
的时候,才会调用 .then
方法。如下,即最长时间 128ms
之后,才会执行 .then
Copy // `delay`毫秒后执行resolve
function timerPromisefy(delay) {
return new Promise(function (resolve) {
setTimeout(function () {
resolve(delay);
}, delay);
});
}
var startDate = Date.now();
Promise.all([
timerPromisefy(1),
timerPromisefy(32),
timerPromisefy(64),
timerPromisefy(128)
]).then(function (values) {
console.log(Date.now() - startDate + 'ms');
// 約128ms
console.log(values); // [1,32,64,128]
});
Promise.race()
: 只要有 一个promise对象
变为 fulfilled
或者 rejected
状态,就会调用后续的 .then
或者 .catch
处理。如下:
4ms之后 第一个promise
进行 resolve
,因此 Promise.race
进行后续的处理。但是 loserPromise
这个 promise
依然会执行,并不会因为 race
而被取消。
Copy var winnerPromise = new Promise(function (resolve) {
setTimeout(function () {
console.log('this is winner');
resolve('this is winner');
}, 4);
});
var loserPromise = new Promise(function (resolve) {
setTimeout(function () {
console.log('this is loser'); // 依然会打印
resolve('this is loser');
}, 1000);
});
Promise.race([winnerPromise, loserPromise]).then(function (value) {
console.log(value); // => 'this is winner'
});
2.12 使用 .then(undefined, onRejected) 还是 .catch(onRejected)
Copy Promise.resolve(1).then(() => {
return Promise.reject('bad')
}, (err) => {
// 不打印,即使用.then(第一个参数,第二个参数),则第二个参数不能捕获第一个参数的异常。只能继续在后续添加.then去捕获
console.log(err)
}).then(undefined,(err) => {
console.log('err',err) // 这里能打印 bad
})
// 使用.catch能捕获.then的第一个参数中所抛出的异常
Promise.resolve(1).then(() => {
return Promise.reject('good')
}).catch((err) => {
console.log(err)
})
即如果使用 then
的方式,如果 onFulfilled
中抛出异常,是不能被当前的 onRejected
捕获的,因此使用 .catch
是更好的处理错误方式。
3. Promise Advanced
Promises/A+
是 ES6 Promises
的前身,如果一个类库兼容 Promise/A+
的话,那么说明除了 then
方法之外,还支持 Promise.all和catch
。
3.1 类库 bluebird
这个类库除了兼容 Promise
规范之外,还扩展了 取消promise
对象的运行,取得 promise
的运行进度,以及 错误处理的扩展检测
等非常丰富的功能。
3.2 使用 reject 还是 throw error
Promise
的构造函数,以及then调用的函数都可以认为是在try catch
代码块中执行的,所以这些代码内部使用throw
,程序不会因为异常而终止。
Copy new Promise(() => {
throw new Error('123')
})
console.log(456) // 依旧会打印456
throw new Error('123')
console.log(456) // 因为异常,不打印
在 promise
中使用 throw
,会被try catch
住,最终 promise
变为 rejected
状态。
Copy var promise = new Promise(function(resolve, reject){
throw new Error("message");
});
promise.catch(function(error){
console.error(error);// => "message"
});
3.3 在then中使用reject
Copy Promise.resolve(1).then(val => {
return new Promise((res,rej) => {
setTimeout(() => {
rej(12);
}, 1000)
})
}).catch(e => {
console.log(e) // 12
})
Promise.resolve(1).then(val => {
setTimeout(() => {
Promise.reject(12); // 无效,即 promise 不会等待定时器触发
}, 1000)
}).catch(e => {
console.log(e)
})
Promise.resolve(1).then(val => {
return Promise.reject(123) // 直接使用reject
}).catch(e => {
console.log(e) // 123
})
3.4 处理超时
Copy function delayPromise(ms) {
return new Promise(function (resolve) {
setTimeout(resolve, ms);
});
}
function timeoutPromise(promise, ms) {
var timeout = delayPromise(ms).then(function () {
throw new Error('Operation timed out after ' + ms + ' ms');
});
return Promise.race([promise, timeout]);
}
// 运行示例
var taskPromise = new Promise(function(resolve){
// 随便一些什么处理
var delay = Math.random() * 2000;
setTimeout(function(){
resolve(delay + "ms");
}, delay);
});
timeoutPromise(taskPromise, 1000).then(function(value){
console.log("taskPromise在规定时间内结束 : " + value);
}).catch(function(error){
console.log("发生超时", error);
});
3.4.1 超时取消xhr请求
Copy 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);
});
3.5 Promise#done
Copy Promise.resolve(1).then(()=> {
console.lg(1) // 不会报错,因为promise代码类似在一个大的try catch中运行,所以即使console.log拼错了,也不会被报出来,除非手动加上catch
})
Promise.resolve(1).then(()=> {
console.lg(1)
}).catch(ee => {
console.log(ee) // 报错 console.lg is not a function
})
这种被内部消化的问题叫做 unhandled rejection
,即rejected时没有找到响应的处理程序。
promise.prototype.done
的源码
Copy if (typeof Promise.prototype.done === 'undefined') {
Promise.prototype.done = function (onFulfilled, onRejected) {
this.then(onFulfilled, onRejected).catch(function (error) {
setTimeout(function () {
throw error;
}, 0);
});
};
}
使用起来和 .then
一样,但是不会返回一个 promise
值,因此不能链式调用,即只能出现在方法链的最后面。因此:
Copy if (typeof Promise.prototype.done === 'undefined') {
Promise.prototype.done = function (onFulfilled, onRejected) {
this.then(onFulfilled, onRejected).catch(function (error) {
setTimeout(function () {
throw error;
}, 0);
});
};
}
Promise.resolve(1).done(()=> {
console.lg(1) //此时抛出异常
})
3.6 More
4.7,4.8 两节应该多看!关于用Promise包装API以及顺序处理Promise的问题
4. 参考资料