什么是Promise?
Promise是抽象异步处理对象以及对其进行各种操作的组件。如果说到基于JavaScript的异步处理,我想大多数都会想到利用回调函数,node.js等规定在JavaScript的回调函数的第一个参数为Error对象,但这只是编码规范,并不具有强制性,即使采取不同的写法也不会出错,并且采取这种方式处理异步容易造成多重嵌套,引发回调地狱问题。Promise则是把类似的异步处理对象和处理规则进行规范化,在采取规定方法之外的写法都会出错。
let promise = getAsyncPromise("test.txt");
promise.then(function(result){
// 获取文件内容成功时的处理
}).catch(function(error){
// 获取文件内容失败时的处理
});除了promise对象规定的方法(then或catch)以外的方法都是不可以使用的。这样就可以将复杂的异步处理进行模式化,更加清晰明了,避免了回调地狱的问题和规范性的问题。
promise简介
constructor
Promise类似于XMLHttpRequest, 从构造函数Promise来创建一个新的promise对象作为接口。
const promise = new Promise(function (resolve, reject) {
// 异步处理
// 处理结束后调用resolve或reject
})
promise.then(onFullfilled, onRejected)resolve(成功时): onFullfilled会被调用
reject(失败时): onRejected会被调用
promise.then成功和失败时都可以使用,如果只想对异常进行处理可以采用promise.then(undefind, onRerjected)这种方式,不过这种情况下promise.catch(onRejected)应该是更好的选择。
除此之外,Promise这样的全局对象还拥有一些静态方法,如:Promise.all和Promise.resolve()
promise的状态
用new Promise实例化的promise对象有三个状态。
resolve(成功时),会调用onFulfilled, reject(失败时),会调用onRejected, pending(初始状态)。
创建一个Promise对象
function getURL(url){
return new Promise(function (resolve, reject){
const 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.send()
}
})
}
// 运行实例
const url = 'http://test.con/get'
getURL(url).then(function onFulfilled(value){
console.log(value)
}).catch(function onRejected(error) {
console.log(value)
})getURL只有状态码为200的情况下才会调用resolve,其他情况则调用reject方法。

new Promise的快捷方式
静态方法Promise.resolve(value)可以认为是new Promise方法的快捷方式。
Promise.resolve(200)
// 等价于
new Promise(function(resolve){
resolve(200)
})Promise.resolve(value)的返回值也是一个Promise对象,可以对其返回值进.then调用
Promise.resolve(42).then(function(value){
console.log(value);
});这种快捷方式在进行Promise对象初始化或者编写测试代码时都比较方便。
Thenable(类Promise对象)
Promise.resolve方法的另一个作用就是将thenable对象转换为Promise对象。就像我们将.length的非数组对象称为类数组对象一样,thenable指的是一个具有.then方法的对象。
如:jQuery.ajax()的返回值是thenable对象,这个对象具有.then方法。
将thenable对象转换为promise对象
var promise = Promise.resolve($.ajax('/json/comment.json'));// => promise对象
promise.then(function(value){
console.log(value);
});Promise.reject
Promise.reject是与promise.resolve类似的静态方法,不同的是,该方法调用的onRejected函数,并将错误对象传递给这个onRejected函数。
Promise只能进行异步操作?
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
// 执行顺序
inner promise // 1
outer promise // 2
42 // 3事实上只有.then中的方法是异步进行的。
Promise链式调用
除了.then().catch()的这种链式调用的方法,其实Promise可以将任意个方法连在一起作为一个方法链。
aPromise.then(function taskA(value){
// task A
}).then(function taskB(vaue){
// task B
}).catch(function onRejected(error){
console.log(error);
});方法链的执行顺序
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");
}
var promise = Promise.resolve();
promise
.then(taskA)
.then(taskB)
.catch(onRejected)
.then(finalTask);
Promise方法链中如何传递参数
前面例子中Task都是相互独立的,只是简单被调用而已。如果TaskA想给TaskB传递一个参数该怎么办呢?
其实TaskA中return的返回值,会在TaskB执行时传给他。
function doubleUp(value) {
return value * 2;
}
function increment(value) {
return value + 1;
}
function output(value) {
console.log(value);// => (1 + 1) * 2
}
const promise = Promise.resolve(1);
promise
.then(increment)
.then(doubleUp)
.then(output)
.catch(function(error){
// promise chain中出现异常的时候会被调用
console.error(error);
});Promise.resolve(1);传递 1 给increment函数- 函数
increment对接收的参数进行 +1 操作并返回(通过return) - 这时参数变为2,并再次传给
doubleUp函数 - 最后在函数
output中打印结果

每个方法return的值不仅只局限于字符串或数值类型,也可以是对象或Promise对象等复杂类型。returnd的值会由Promise.resolve进行相应的包装处理,因此不管返回的值是什么样的,最终.then的结果都是返回一个新创建的Promise对象。
Promise#catch
实际上Promise#catch只是Promise.then(undefined, onRejected)方法的一个别名而已。
var promise = Promise.reject(new Error("message"));
promise.catch(function (error) {
console.error(error);
});每次调用then都会返回一个新创建的promise对象
var aPromise = new Promise(function (resolve) {
resolve(100);
});
var thenPromise = aPromise.then(function (value) {
console.log(value);
});
var catchPromise = thenPromise.catch(function (error) {
console.error(error);
});
console.log(aPromise !== thenPromise); // => true
console.log(thenPromise !== catchPromise);// => true
Promise.all
如果多个Promise对象都变为FuFilled状态的时候再进行下一步操作,该如何处理呢?
promise.all接收一个 promise对象的数组作为参数,当这个数组里的所有promise对象全部变为resolve或reject状态的时候,它才会去调用 .then 方法。
// `delay`毫秒后执行resolve
function timerPromisefy(delay) {
return new Promise(function (resolve) {
setTimeout(function () {
resolve(delay);
}, delay);
});
}
var startDate = Date.now();
// 所有promise变为resolve后程序退出
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]
});timerPromisefy 会每隔一定时间(通过参数指定)之后,返回一个promise对象,状态为FulFilled,其状态值为传给 timerPromisefy 的参数。
而传给 Promise.all 的则是由上述promise组成的数组。
var promises = [
timerPromisefy(1),
timerPromisefy(32),
timerPromisefy(64),
timerPromisefy(128)
];这时候,每隔1, 32, 64, 128 ms都会有一个promise发生 resolve 行为。
也就是说,这个promise对象数组中所有promise都变为resolve状态的话,至少需要128ms。实际我们计算一下Promise.all 的执行时间的话,它确实是消耗了128ms的时间。
从上述结果可以看出,传递给Promise.all的promise并不是一个个的顺序执行的,而是同时开始、并行执行的。
Promise.race
接着我们来看看和Promise.all 类似的对多个promise对象进行处理的 Promise.race 方法。
它的使用方法和Promise.all一样,接收一个promise对象数组为参数。
Promise.all 在接收到的所有的对象promise都变为 FulFilled 或者 Rejected 状态之后才会继续进行后面的处理, 与之相对的是 Promise.race 只要有一个promise对象进入 FulFilled 或者 Rejected 状态的话,就会继续进行后面的处理。
像Promise.all时的例子一样,我们来看一个带计时器的 Promise.race 的使用例子。
// `delay`毫秒后执行resolve
function timerPromisefy(delay) {
return new Promise(function (resolve) {
setTimeout(function () {
resolve(delay);
}, delay);
});
}
// 任何一个promise变为resolve或reject 的话程序就停止运行
Promise.race([
timerPromisefy(1),
timerPromisefy(32),
timerPromisefy(64),
timerPromisefy(128)
]).then(function (value) {
console.log(value); // => 1
});上面的代码创建了4个promise对象,这些promise对象会分别在1ms,32ms,64ms和128ms后变为确定状态,即FulFilled,并且在第一个变为确定状态的1ms后, .then 注册的回调函数就会被调用,这时候确定状态的promise对象会调用 resolve(1) 因此传递给 value 的值也是1,控制台上会打印出1来。
手写一个Promise
const RESOLVED = 'RESOLVED'; // 成功
const REJECTED = 'REJECTED'; // 失败
const PENDING = 'PENDING'; // 等待态
const resolvePromise = (promise2, x, resolve, reject) => {
// 1.循环引用 自己等待自己完成 错误的实现
if (promise2 === x) { // 用一个类型错误 结束掉promise
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
// 后续的条件要严格判断 保证代码能和别的库一起使用
let called;
if ((typeof x === 'object' && x != null) || typeof x === 'function') { // 有可能是一个promise
// 要继续判断
try {
let then = x.then;
if (typeof then === 'function') { // 只能认为是一个promise了
// 不要写成x.then 直接then.call就可以了 因为x.then 会再次取值
then.call(x, y => { // 根据promise的状态决定是成功还是失败
if (called) return;
called = true;
resolvePromise(promise2, y, resolve, reject); // 递归解析的过程
}, e => {
if (called) return;
called = true;
reject(e); // 只要失败就失败
});
} else { // {then:'23'}
resolve(x);
}
} catch (e) { // 防止失败了再次进入成功
if (called) return;
called = true;
reject(e); // 取值出错
}
} else {
resolve(x);
}
}
class Promise {
constructor(executor) {
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
this.onResolvedCallbacks = []; // 专门用来存放成功的回调
this.onRejectedCallbacks = []; // 专门用来存放失败的回调
let resolve = (value) => { // 调用此方法就是成功
if(value instanceof Promise){
return value.then(resolve,reject); // 递归解析resolve中的参数,直到这个值是普通值
}
if (this.status === PENDING) {
this.value = value;
this.status = RESOLVED;
this.onResolvedCallbacks.forEach(fn => fn());
}
}
let reject = (reason) => {
if (this.status === PENDING) {
this.reason = reason;
this.status = REJECTED;
this.onRejectedCallbacks.forEach(fn => fn());
}
}
try {
executor(resolve, reject); // 立即执行
} catch (e) { // 错误处理 需要直接走错误逻辑
reject(e);
}
}
// 1. promise 成功和失败的回调的返回值 可以传递到外层的下一个then
// 2. 如果返回的是普通值的话 (传递到下一次的成功中,不是错误不是promise就是普通值) ,出错的情况(一定会走到下一次的失败),可能还要promise的情况(会采用promise的状态,决定走下一次的成功还是失败 )
// 3.错误处理 如果离自己最近的then 没有错误处理(没有写错误函数) 会向下找
// 4. 每次执行完promise.then方法后返回的都是一个“新的promise”
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
let promise2 = new Promise((resolve, reject) => { // 为了实现链式调用
if (this.status === RESOLVED) {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
// x可能是一个proimise
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
if (this.status === REJECTED) {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
if (this.status === PENDING) {
this.onResolvedCallbacks.push(() => {
// todo...
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
// todo...
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
}
});
return promise2;
}
catch(errCallback){
return this.then(null,errCallback)
}
static resolve(data){
return new Promise((resolve,reject)=>{
resolve(data);
})
}
static reject(reason){
return new Promise((resolve,reject)=>{
reject(reason);
})
}
}
// promise的延迟对象
Promise.defer = Promise.deferred = function () {
let dfd = {};
dfd.promise = new Promise((resolve,reject)=>{
dfd.resolve = resolve;
dfd.reject = reject;
})
return dfd
}
module.exports = Promise;