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 codeif (/* 异步操作成功 */){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: testconst 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; // 保存thisself.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状态为resolvedif (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状态变为rejectedreturn reject(e);}});}));}// promise状态为rejectedif (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状态变为rejectedreturn 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状态变为rejectedreturn reject(e);}},onRejected(reason) {try {// 调用then方法的onResolved回调const x = onRejected(reason);// 根据x的值修改promise2的状态resolvePromise(promise2, x, resolve, reject);} catch (e) {// promise2状态变为rejectedreturn 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 compliancemodule.exports = Promise;} catch (e) {}return Promise;}());
