Promise 是异步编程的一种解决方案
Promise的诞生与Javascript中异步编程息息相关,js中异步编程主要指的是setTimout/setInterval、DOM事件机制、ajax,通过传入回调函数实现控制反转。异步编程为js带来强大灵活性的同时,也带来了嵌套回调的问题。详细来说主要有两点,第一嵌套太深代码可读性太差,第二并行逻辑必须串行执行。
从语法上说,Promise 是一个对象,它可以获取异步操作的消息,每个Promise对象代表一个异步操作,并且它有三种状态,pending(进行中)、fulfilled(已成功)和rejected(已失败)。
fulfilled 美 [fʊlˈfɪld]
reject 美 [rɪˈdʒɛkt]
Promise对象有以下两个特点。
1.对象的状态不受外界影响。
2.一旦状态改变,就不会再变。
下面resolved状态表示fulfilled状态;
Promise也有一些缺点。首先,无法取消Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。第三,当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
Promise.prototype.then(f1: function, f2: function)
Promise.prototype.catch(f1:function)
代替.then(null, rejection)或.then(undefined, rejection)如果异步操作抛出错误,状态就会变为rejected,就会调用catch方法指定的回调函数
Promise.prototype.finally(f1: function)
方法用于指定不管 Promise 对象最后状态如何,都会执行的操作
Promise.all([promise: Promse, …])
Promise.all方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
Promise.race([promise: Promse, …])
美 [res] Promise.race方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。Promise.race方法的参数与Promise.all方法一样,如果不是 Promise 实例,就会先调用下面讲到的Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。如果指定时间内没有获得结果,就将 Promise 的状态变为reject,否则变为resolve。
const p = Promise.race([
fetch('/resource-that-may-take-a-while'),
new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('request timeout')), 5000)
})
]);
p
.then(console.log)
.catch(console.error);
上面代码中,如果 5 秒之内fetch方法无法返回结果,变量p的状态就会变为rejected,从而触发catch方法指定的回调函数。
Project.resolve(obj : any)
Promise.resolve() 美 [rɪˈzɑ:lv] 有时需要将现有对象转为 Promise 对象,Promise.resolve方法就起到这个作用。
(1)参数是一个 Promise 实例,直接返回
(2)参数是一个thenable对象
thenable对象指的是具有then方法的对象,比如下面这个对象。
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
};
Promise.resolve方法会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then方法。
(3)参数不是具有then方法的对象,或根本就不是对象
如果参数是一个原始值,或者是一个不具有then方法的对象,则Promise.resolve方法返回一个新的 Promise 对象,状态为resolved。
const p = Promise.resolve('Hello');
p.then(function (s){
console.log(s)
});
// Hello
(4)不带有任何参数
Promise.resolve方法允许调用时不带参数,直接返回一个resolved状态的 Promise 对象。
所以,如果希望得到一个 Promise 对象,比较方便的方法就是直接调用Promise.resolve方法。
const p = Promise.resolve();
p.then(function () {
// ...
});
上面代码的变量p就是一个 Promise 对象。
需要注意的是,立即resolve的 Promise 对象,是在本轮“事件循环”(event loop)的结束时,而不是在下一轮“事件循环”的开始时。
setTimeout(function () {
console.log('three');
}, 0);
Promise.resolve().then(function () {
console.log('two');
});
console.log('one');
// one
// two
// three
上面代码中,setTimeout(fn, 0)在下一轮“事件循环”开始时执行,Promise.resolve()在本轮“事件循环”结束时执行,console.log(‘one’)则是立即执行,因此最先输出。
Promise.reject(fn1: function)
美 [rɪˈdʒɛkt] Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected
Promise.try(obj: any);
8.Promise.try()) 美 [traɪ]
实际开发中,经常遇到一种情况:不知道或者不想区分,函数f是同步函数还是异步操作,但是想用 Promise 来处理它。因为这样就可以不管f是否包含异步操作,都用then方法指定下一步流程,用catch方法处理f抛出的错误。但是这样做f如果是一个同步函数的话也会被当作异步执行了,那如何让同步函数同步执行,异步函数异步执行
// 方法一
(async () => f())()
.then(...)
.catch(...)
// 方法二
const f = () => console.log('now');
(
() => new Promise(
resolve => resolve(f())
)
)();
console.log('next');
// now
// next
方法三
const f = () => console.log('now');
Promise.try(f);
console.log('next');
Promise.try(() => database.users.get({id: userId}))
.then(...)
.catch(...)
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
promise.then(function(value) {
// success
}, function(error) {
// failure
});
// 异步加载图片
function loadImageAsync(url) {
return new Promise(function(resolve, reject) {
const image = new Image();
image.onload = function() {
resolve(image);
};
image.onerror = function() {
reject(new Error('Could not load image at ' + url));
};
image.src = url;
});
}
const p1 = new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('fail')), 3000)
})
const p2 = new Promise(function (resolve, reject) {
setTimeout(() => resolve(p1), 1000)
})
p2
.then(result => console.log(result))
.catch(error => console.log(error))
Promise.prototype.then(resolved, rejected); // 返回一个新的Promise对象
getJSON("/posts.json").then(function(json) {
return json.post;
}).then(function(post) {
// ...
});
/*
上面的代码使用then方法,依次指定了两个回调函数。
第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数。
采用链式的then,可以指定一组按照次序调用的回调函数。
*/
Promise.prototype.catch方法是.then(null, rejection)或.then(undefined, rejection)的别名
const promise = new Promise(function(resolve, reject) {
throw new Error('test');
});
promise.catch(function(error) {
console.log(error);
});
// Error: test
const promise = new Promise(function(resolve, reject) {
try {
throw new Error('test');
} catch(e) {
reject(e);
}
});
promise.catch(function(error) {
console.log(error);
}); // 与上面等价
应用
加载图片
我们可以将图片的加载写成一个Promise
,一旦加载完成,Promise
的状态就发生变化。
const preloadImage = function (path) {
return new Promise(function (resolve, reject) {
const image = new Image();
image.onload = resolve;
image.onerror = reject;
image.src = path;
});
};
Generator 函数与 Promise 的结合
使用 Generator 函数管理流程,遇到异步操作的时候,通常返回一个Promise
对象。
function * demo (){
var a = yield Promise.resolve(1);
var b = yield Promise.resolve(a + 1);
var c = yield Promise.resolve(b + 1);
return c;
}
function co(generator) {
var gen = generator();
function nextFunc(arg) {
console.log('arg', arg);
var next = gen.next(arg);
if (!next.done) {
next.value.then(function(data){
console.log('data',data);
nextFunc(data);
});
} else if (next.value) {
console.log('return',next.value)
return next.value;
}
}
nextFunc();
}
co(demo);
Async/Await与promise结合使用
async function es7(){
var a = await Promise.resolve(1);
var b = await Promise.resolve(a + 1);
var c = await Promise.resolve(b + 1);
return c
}
es7().then((data)=>{
console.log(data);
})
==============================================================================
promise完整源码实现
const Promise = (function () {
function Promise(resolver) {
if (typeof resolver !== 'function') { // resolver必须是函数
throw new TypeError(`Promise resolver ${resolver} is not a function`);
}
if (!(this instanceof Promise)) return new Promise(resolver);
const self = this; // 保存this
self.callbacks = []; // 保存onResolve和onReject函数集合
self.status = 'pending'; // 当前状态
function resolve(value) {
setTimeout(() => { // 异步调用
if (self.status !== 'pending') {
return;
}
self.status = 'resolved'; // 修改状态
self.data = value;
for (let i = 0; i < self.callbacks.length; i++) {
self.callbacks[i].onResolved(value);
}
});
}
function reject(reason) {
setTimeout(() => { // 异步调用
if (self.status !== 'pending') {
return;
}
self.status = 'rejected'; // 修改状态
self.data = reason;
for (let i = 0; i < self.callbacks.length; i++) {
self.callbacks[i].onRejected(reason);
}
});
}
try {
resolver(resolve, reject); // 执行resolver函数
} catch (e) {
reject(e);
}
}
function resolvePromise(promise, x, resolve, reject) {
let then;
let thenCalledOrThrow = false;
if (promise === x) {
return reject(new TypeError('Chaining cycle detected for promise!'));
}
if ((x !== null) && ((typeof x === 'object') || (typeof x === 'function'))) {
try {
then = x.then;
if (typeof then === 'function') {
then.call(x, (y) => {
if (thenCalledOrThrow) return;
thenCalledOrThrow = true;
return resolvePromise(promise, y, resolve, reject);
}, (r) => {
if (thenCalledOrThrow) return;
thenCalledOrThrow = true;
return reject(r);
});
} else {
return resolve(x);
}
} catch (e) {
if (thenCalledOrThrow) return;
thenCalledOrThrow = true;
return reject(e);
}
} else {
return resolve(x);
}
}
Promise.prototype.then = function (onResolved, onRejected) {
// 健壮性处理,处理点击穿透
onResolved = typeof onResolved === 'function' ? onResolved : function (v) { return v; };
onRejected = typeof onRejected === 'function' ? onRejected : function (r) { throw r; };
const self = this;
let promise2;
// promise状态为resolved
if (self.status === 'resolved') {
return promise2 = new Promise(((resolve, reject) => {
setTimeout(() => {
try {
// 调用then方法的onResolved回调
const x = onResolved(self.data);
// 根据x的值修改promise2的状态
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
// promise2状态变为rejected
return reject(e);
}
});
}));
}
// promise状态为rejected
if (self.status === 'rejected') {
return promise2 = new Promise(((resolve, reject) => {
setTimeout(() => {
try {
// 调用then方法的onReject回调
const x = onRejected(self.data);
// 根据x的值修改promise2的状态
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
// promise2状态变为rejected
return reject(e);
}
});
}));
}
// promise状态为pending
// 需要等待promise的状态改变
if (self.status === 'pending') {
return promise2 = new Promise(((resolve, reject) => {
self.callbacks.push({
onResolved(value) {
try {
// 调用then方法的onResolved回调
const x = onResolved(value);
// 根据x的值修改promise2的状态
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
// promise2状态变为rejected
return reject(e);
}
},
onRejected(reason) {
try {
// 调用then方法的onResolved回调
const x = onRejected(reason);
// 根据x的值修改promise2的状态
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
// promise2状态变为rejected
return reject(e);
}
}
});
}));
}
};
// 获取当前Promise传递的值
Promise.prototype.valueOf = function () {
return this.data;
};
// 由then方法实现catch方法
Promise.prototype.catch = function (onRejected) {
return this.then(null, onRejected);
};
// finally方法
Promise.prototype.finally = function (fn) {
return this.then((v) => {
setTimeout(fn);
return v;
}, (r) => {
setTimeout(fn);
throw r;
});
};
Promise.prototype.spread = function (fn, onRejected) {
return this.then(values => fn(...values), onRejected);
};
Promise.prototype.inject = function (fn, onRejected) {
return this.then(v => fn(...fn.toString().match(/\((.*?)\)/)[1].split(',').map(key => v[key])), onRejected);
};
Promise.prototype.delay = function (duration) {
return this.then(value => new Promise(((resolve, reject) => {
setTimeout(() => {
resolve(value);
}, duration);
})), reason => new Promise(((resolve, reject) => {
setTimeout(() => {
reject(reason);
}, duration);
})));
};
Promise.all = function (promises) {
return new Promise(((resolve, reject) => {
let resolvedCounter = 0;
const promiseNum = promises.length;
const resolvedValues = new Array(promiseNum);
for (let i = 0; i < promiseNum; i++) {
(function (i) {
Promise.resolve(promises[i]).then((value) => {
resolvedCounter++;
resolvedValues[i] = value;
if (resolvedCounter == promiseNum) {
return resolve(resolvedValues);
}
}, reason => reject(reason));
}(i));
}
}));
};
Promise.race = function (promises) {
return new Promise(((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
Promise.resolve(promises[i]).then(value => resolve(value), reason => reject(reason));
}
}));
};
Promise.resolve = function (value) {
var promise = new Promise(((resolve, reject) => {
resolvePromise(promise, value, resolve, reject);
}));
return promise;
};
Promise.reject = function (reason) {
return new Promise(((resolve, reject) => {
reject(reason);
}));
};
Promise.fcall = function (fn) {
// 虽然fn可以接收到上一层then里传来的参数,但是其实是undefined,所以跟没有是一样的,因为resolve没参数啊
return Promise.resolve().then(fn);
};
Promise.done = Promise.stop = function () {
return new Promise((() => {}));
};
Promise.deferred = Promise.defer = function () {
const dfd = {};
dfd.promise = new Promise(((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
}));
return dfd;
};
try { // CommonJS compliance
module.exports = Promise;
} catch (e) {}
return Promise;
}());