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)

then()[ðɛn] 方法

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。

  1. const p = Promise.race([
  2. fetch('/resource-that-may-take-a-while'),
  3. new Promise(function (resolve, reject) {
  4. setTimeout(() => reject(new Error('request timeout')), 5000)
  5. })
  6. ]);
  7. p
  8. .then(console.log)
  9. .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方法的对象,比如下面这个对象。

  1. let thenable = {
  2. then: function(resolve, reject) {
  3. resolve(42);
  4. }
  5. };

Promise.resolve方法会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then方法。
(3)参数不是具有then方法的对象,或根本就不是对象
如果参数是一个原始值,或者是一个不具有then方法的对象,则Promise.resolve方法返回一个新的 Promise 对象,状态为resolved。

  1. const p = Promise.resolve('Hello');
  2. p.then(function (s){
  3. console.log(s)
  4. });
  5. // Hello

(4)不带有任何参数
Promise.resolve方法允许调用时不带参数,直接返回一个resolved状态的 Promise 对象。
所以,如果希望得到一个 Promise 对象,比较方便的方法就是直接调用Promise.resolve方法。

  1. const p = Promise.resolve();
  2. p.then(function () {
  3. // ...
  4. });

上面代码的变量p就是一个 Promise 对象。
需要注意的是,立即resolve的 Promise 对象,是在本轮“事件循环”(event loop)的结束时,而不是在下一轮“事件循环”的开始时。

  1. setTimeout(function () {
  2. console.log('three');
  3. }, 0);
  4. Promise.resolve().then(function () {
  5. console.log('two');
  6. });
  7. console.log('one');
  8. // one
  9. // two
  10. // 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如果是一个同步函数的话也会被当作异步执行了,那如何让同步函数同步执行,异步函数异步执行

  1. // 方法一
  2. (async () => f())()
  3. .then(...)
  4. .catch(...)
  5. // 方法二
  6. const f = () => console.log('now');
  7. (
  8. () => new Promise(
  9. resolve => resolve(f())
  10. )
  11. )();
  12. console.log('next');
  13. // now
  14. // next
  15. 方法三
  16. const f = () => console.log('now');
  17. Promise.try(f);
  18. console.log('next');
  19. Promise.try(() => database.users.get({id: userId}))
  20. .then(...)
  21. .catch(...)
  1. const promise = new Promise(function(resolve, reject) {
  2. // ... some code
  3. if (/* 异步操作成功 */){
  4. resolve(value);
  5. } else {
  6. reject(error);
  7. }
  8. });
  9. promise.then(function(value) {
  10. // success
  11. }, function(error) {
  12. // failure
  13. });
  14. // 异步加载图片
  15. function loadImageAsync(url) {
  16. return new Promise(function(resolve, reject) {
  17. const image = new Image();
  18. image.onload = function() {
  19. resolve(image);
  20. };
  21. image.onerror = function() {
  22. reject(new Error('Could not load image at ' + url));
  23. };
  24. image.src = url;
  25. });
  26. }
  27. const p1 = new Promise(function (resolve, reject) {
  28. setTimeout(() => reject(new Error('fail')), 3000)
  29. })
  30. const p2 = new Promise(function (resolve, reject) {
  31. setTimeout(() => resolve(p1), 1000)
  32. })
  33. p2
  34. .then(result => console.log(result))
  35. .catch(error => console.log(error))

Promise.prototype.then(resolved, rejected); // 返回一个新的Promise对象

  1. getJSON("/posts.json").then(function(json) {
  2. return json.post;
  3. }).then(function(post) {
  4. // ...
  5. });
  6. /*
  7. 上面的代码使用then方法,依次指定了两个回调函数。
  8. 第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数。
  9. 采用链式的then,可以指定一组按照次序调用的回调函数。
  10. */

Promise.prototype.catch方法是.then(null, rejection)或.then(undefined, rejection)的别名

  1. const promise = new Promise(function(resolve, reject) {
  2. throw new Error('test');
  3. });
  4. promise.catch(function(error) {
  5. console.log(error);
  6. });
  7. // Error: test
  8. const promise = new Promise(function(resolve, reject) {
  9. try {
  10. throw new Error('test');
  11. } catch(e) {
  12. reject(e);
  13. }
  14. });
  15. promise.catch(function(error) {
  16. console.log(error);
  17. }); // 与上面等价

应用

加载图片

我们可以将图片的加载写成一个Promise,一旦加载完成,Promise的状态就发生变化。

  1. const preloadImage = function (path) {
  2. return new Promise(function (resolve, reject) {
  3. const image = new Image();
  4. image.onload = resolve;
  5. image.onerror = reject;
  6. image.src = path;
  7. });
  8. };

Generator 函数与 Promise 的结合

使用 Generator 函数管理流程,遇到异步操作的时候,通常返回一个Promise对象。

  1. function * demo (){
  2. var a = yield Promise.resolve(1);
  3. var b = yield Promise.resolve(a + 1);
  4. var c = yield Promise.resolve(b + 1);
  5. return c;
  6. }
  7. function co(generator) {
  8. var gen = generator();
  9. function nextFunc(arg) {
  10. console.log('arg', arg);
  11. var next = gen.next(arg);
  12. if (!next.done) {
  13. next.value.then(function(data){
  14. console.log('data',data);
  15. nextFunc(data);
  16. });
  17. } else if (next.value) {
  18. console.log('return',next.value)
  19. return next.value;
  20. }
  21. }
  22. nextFunc();
  23. }
  24. co(demo);

Async/Await与promise结合使用

  1. async function es7(){
  2. var a = await Promise.resolve(1);
  3. var b = await Promise.resolve(a + 1);
  4. var c = await Promise.resolve(b + 1);
  5. return c
  6. }
  7. es7().then((data)=>{
  8. console.log(data);
  9. })

==============================================================================

promise完整源码实现

  1. const Promise = (function () {
  2. function Promise(resolver) {
  3. if (typeof resolver !== 'function') { // resolver必须是函数
  4. throw new TypeError(`Promise resolver ${resolver} is not a function`);
  5. }
  6. if (!(this instanceof Promise)) return new Promise(resolver);
  7. const self = this; // 保存this
  8. self.callbacks = []; // 保存onResolve和onReject函数集合
  9. self.status = 'pending'; // 当前状态
  10. function resolve(value) {
  11. setTimeout(() => { // 异步调用
  12. if (self.status !== 'pending') {
  13. return;
  14. }
  15. self.status = 'resolved'; // 修改状态
  16. self.data = value;
  17. for (let i = 0; i < self.callbacks.length; i++) {
  18. self.callbacks[i].onResolved(value);
  19. }
  20. });
  21. }
  22. function reject(reason) {
  23. setTimeout(() => { // 异步调用
  24. if (self.status !== 'pending') {
  25. return;
  26. }
  27. self.status = 'rejected'; // 修改状态
  28. self.data = reason;
  29. for (let i = 0; i < self.callbacks.length; i++) {
  30. self.callbacks[i].onRejected(reason);
  31. }
  32. });
  33. }
  34. try {
  35. resolver(resolve, reject); // 执行resolver函数
  36. } catch (e) {
  37. reject(e);
  38. }
  39. }
  40. function resolvePromise(promise, x, resolve, reject) {
  41. let then;
  42. let thenCalledOrThrow = false;
  43. if (promise === x) {
  44. return reject(new TypeError('Chaining cycle detected for promise!'));
  45. }
  46. if ((x !== null) && ((typeof x === 'object') || (typeof x === 'function'))) {
  47. try {
  48. then = x.then;
  49. if (typeof then === 'function') {
  50. then.call(x, (y) => {
  51. if (thenCalledOrThrow) return;
  52. thenCalledOrThrow = true;
  53. return resolvePromise(promise, y, resolve, reject);
  54. }, (r) => {
  55. if (thenCalledOrThrow) return;
  56. thenCalledOrThrow = true;
  57. return reject(r);
  58. });
  59. } else {
  60. return resolve(x);
  61. }
  62. } catch (e) {
  63. if (thenCalledOrThrow) return;
  64. thenCalledOrThrow = true;
  65. return reject(e);
  66. }
  67. } else {
  68. return resolve(x);
  69. }
  70. }
  71. Promise.prototype.then = function (onResolved, onRejected) {
  72. // 健壮性处理,处理点击穿透
  73. onResolved = typeof onResolved === 'function' ? onResolved : function (v) { return v; };
  74. onRejected = typeof onRejected === 'function' ? onRejected : function (r) { throw r; };
  75. const self = this;
  76. let promise2;
  77. // promise状态为resolved
  78. if (self.status === 'resolved') {
  79. return promise2 = new Promise(((resolve, reject) => {
  80. setTimeout(() => {
  81. try {
  82. // 调用then方法的onResolved回调
  83. const x = onResolved(self.data);
  84. // 根据x的值修改promise2的状态
  85. resolvePromise(promise2, x, resolve, reject);
  86. } catch (e) {
  87. // promise2状态变为rejected
  88. return reject(e);
  89. }
  90. });
  91. }));
  92. }
  93. // promise状态为rejected
  94. if (self.status === 'rejected') {
  95. return promise2 = new Promise(((resolve, reject) => {
  96. setTimeout(() => {
  97. try {
  98. // 调用then方法的onReject回调
  99. const x = onRejected(self.data);
  100. // 根据x的值修改promise2的状态
  101. resolvePromise(promise2, x, resolve, reject);
  102. } catch (e) {
  103. // promise2状态变为rejected
  104. return reject(e);
  105. }
  106. });
  107. }));
  108. }
  109. // promise状态为pending
  110. // 需要等待promise的状态改变
  111. if (self.status === 'pending') {
  112. return promise2 = new Promise(((resolve, reject) => {
  113. self.callbacks.push({
  114. onResolved(value) {
  115. try {
  116. // 调用then方法的onResolved回调
  117. const x = onResolved(value);
  118. // 根据x的值修改promise2的状态
  119. resolvePromise(promise2, x, resolve, reject);
  120. } catch (e) {
  121. // promise2状态变为rejected
  122. return reject(e);
  123. }
  124. },
  125. onRejected(reason) {
  126. try {
  127. // 调用then方法的onResolved回调
  128. const x = onRejected(reason);
  129. // 根据x的值修改promise2的状态
  130. resolvePromise(promise2, x, resolve, reject);
  131. } catch (e) {
  132. // promise2状态变为rejected
  133. return reject(e);
  134. }
  135. }
  136. });
  137. }));
  138. }
  139. };
  140. // 获取当前Promise传递的值
  141. Promise.prototype.valueOf = function () {
  142. return this.data;
  143. };
  144. // 由then方法实现catch方法
  145. Promise.prototype.catch = function (onRejected) {
  146. return this.then(null, onRejected);
  147. };
  148. // finally方法
  149. Promise.prototype.finally = function (fn) {
  150. return this.then((v) => {
  151. setTimeout(fn);
  152. return v;
  153. }, (r) => {
  154. setTimeout(fn);
  155. throw r;
  156. });
  157. };
  158. Promise.prototype.spread = function (fn, onRejected) {
  159. return this.then(values => fn(...values), onRejected);
  160. };
  161. Promise.prototype.inject = function (fn, onRejected) {
  162. return this.then(v => fn(...fn.toString().match(/\((.*?)\)/)[1].split(',').map(key => v[key])), onRejected);
  163. };
  164. Promise.prototype.delay = function (duration) {
  165. return this.then(value => new Promise(((resolve, reject) => {
  166. setTimeout(() => {
  167. resolve(value);
  168. }, duration);
  169. })), reason => new Promise(((resolve, reject) => {
  170. setTimeout(() => {
  171. reject(reason);
  172. }, duration);
  173. })));
  174. };
  175. Promise.all = function (promises) {
  176. return new Promise(((resolve, reject) => {
  177. let resolvedCounter = 0;
  178. const promiseNum = promises.length;
  179. const resolvedValues = new Array(promiseNum);
  180. for (let i = 0; i < promiseNum; i++) {
  181. (function (i) {
  182. Promise.resolve(promises[i]).then((value) => {
  183. resolvedCounter++;
  184. resolvedValues[i] = value;
  185. if (resolvedCounter == promiseNum) {
  186. return resolve(resolvedValues);
  187. }
  188. }, reason => reject(reason));
  189. }(i));
  190. }
  191. }));
  192. };
  193. Promise.race = function (promises) {
  194. return new Promise(((resolve, reject) => {
  195. for (let i = 0; i < promises.length; i++) {
  196. Promise.resolve(promises[i]).then(value => resolve(value), reason => reject(reason));
  197. }
  198. }));
  199. };
  200. Promise.resolve = function (value) {
  201. var promise = new Promise(((resolve, reject) => {
  202. resolvePromise(promise, value, resolve, reject);
  203. }));
  204. return promise;
  205. };
  206. Promise.reject = function (reason) {
  207. return new Promise(((resolve, reject) => {
  208. reject(reason);
  209. }));
  210. };
  211. Promise.fcall = function (fn) {
  212. // 虽然fn可以接收到上一层then里传来的参数,但是其实是undefined,所以跟没有是一样的,因为resolve没参数啊
  213. return Promise.resolve().then(fn);
  214. };
  215. Promise.done = Promise.stop = function () {
  216. return new Promise((() => {}));
  217. };
  218. Promise.deferred = Promise.defer = function () {
  219. const dfd = {};
  220. dfd.promise = new Promise(((resolve, reject) => {
  221. dfd.resolve = resolve;
  222. dfd.reject = reject;
  223. }));
  224. return dfd;
  225. };
  226. try { // CommonJS compliance
  227. module.exports = Promise;
  228. } catch (e) {}
  229. return Promise;
  230. }());