26.12.2014

Почему Native Promise лучше всех.

Простой пример

jQuery

			function deferredExec(func) {
				#! var dfd = $.Deferred();
				#! dfd.resolve(func());
				#! return dfd.promise();
			}
			deferredExec(function () { return "jquery"; })
				.then(function (res) {
					console.log(res); // "jquery"
				});
		

Promise

			function promiseExec(func) {
				#!+ return new Promise(function (resolve) {
					#! resolve(func());
				#!- });
			}
			promiseExec(function () { return "promise"; })
				.then(function (res) {
					console.log(res); // "promise"
				});
		

Сравним

			deferredExec("jquery") // TypeError: string is not a function

			promiseExec("promise") // Тишина
				#!+ .catch(function (err) {
					console.log(err); // TypeError: string is not a function
				#!- });
		

Пффф, это легко исправить

jQuery и try catch

			function deferredExec(func) {
				var dfd = $.Deferred();
				#!+ try {
					#! dfd.resolve(func());
				} catch (err) {
					#! dfd.reject(err);
				#!- }
				return dfd.promise();
			}
			#!+ deferredExec("fail").fail(function (res) {
				#! console.log(res); // TypeError: string is not a function
			#!- });
		

Очевидные минусы

И это не всё

Pause on exceptions

Promise

jQuery

Опять минусы

Как видите «Pause on exceptions» не работает для try catch и это логично, чтобы перехватить это исключение нужно включить «Pause On Caught Exceptions», но это перехват вообще всех исключений, что будет только мешать.

Ещё раз сравним код

			function promiseExec(func) {
				return new Promise(function (resolve) {
					resolve(func());
				});
			}
		
			function deferredExec(func) {
				var dfd = $.Deferred();
				try {
					dfd.resolve(func());
				} catch (err) {
					dfd.reject(err);
				}
				return dfd.promise();
			}
		

Важно!

Важно!

			Promise.reject(1)
				#!+ .then(null, function (val) {
					return val + 1;
				#!- })
				#!+ .then(function () {
					// ?
				}, function () {
					// ?
				#!- });
		
			$.Deferred().reject(1)
				#!+ .then(null, function (val) {
					return val + 1;
				#!- })
				#!+ .then(function () {
					// ?
				}, function () {
					// ?
				#!- });
		

Важно!

			Promise.reject(1)
				.then(null, function (val) {
					return val + 1;
				})
				.then(function (val) {
					// 2 -- success
				}, function () {
					// ?
				});
		
			$.Deferred().reject(1)
				.then(null, function (val) {
					return val + 1;
				})
				.then(function () {
					// ?
				}, function (val) {
					// 2 -- fail
				});
		

Важно!

			Promise.reject(1)
				.then(null, function (val) {
					return val + 1;
				})
				.then(function (val) {
					// 2 -- success
				});
		
			$.Deferred().reject(1)
				.then(null, function (val) {
					#! return $.Deferred().resolve(val + 1);
				})
				#!+ .then(function (val) {
					// 2 -- success
				#!- });
		

Важно! Итого

			Promise.reject(1)
				.catch(function (val) {
					return val + 1;
				})
				.then(function (val) {
					// 2 -- success
				});
		
			$.Deferred().reject(1)
				.then(null, function (val) {
					return $.Deferred().resolve(val + 1);
				})
				.then(function (val) {
					// 2 -- success
				});
		

Минусы Promise

Минусы Promise (с натяжкой)

Способы исправить их

  1. Написать wrapper
  2. Расширить прототип
  3. Создать наследника

Создаем наследника

			function MyPromise() {
				Promise.apply(this, arguments);
			}
			MyPromise.prototype = Object.create(Promise.prototype);
			MyPromise.prototype.constructor = MyPromise;
			MyPromise.prototype.always = function (fn) {
				this.then(fn, fn);
				return this;
			};

			#! var promise = new MyPromise(function () { /* >__< */ });
			#! // TypeError: #<MyPromise> is not a promise
		

Но! Есть трюк

Но! Есть трюк

			function MyPromise(executor) {
				#! var promise = new Promise(executor);
				#! promise.__proto__ = this.__proto__; // ТРЮК!
				#! return promise;
			}

			MyPromise.prototype = Object.create(Promise.prototype);
			MyPromise.prototype.constructor = MyPromise;

			MyPromise.prototype.always = function (fn) {
				this.then(fn, fn);
				return this;
			};
		

FireFox

			new MyPromise(function (resolve) {
				resolve("OK");
			})
				#! .always(function (val) { console.log([1, val]); })
				#! .then(function (val) { return val + "!"; })
				#! .always(function (val) { console.log([2, val]);})
			;

			#!+ // TypeError: (intermediate value).always(...).then(...).always is not a function
			#!- [1, "OK"]
		

FireFix: then

			MyPromise.prototype.then = function () {
				var promise = Promise.prototype.then.apply(this, arguments);
				promise.__proto__ = this.__proto__; // FireFix
				return promise;
			};
		

FireFix: then & catch

			["then", "catch"].forEach(function (method) {
				MyPromise.prototype[method] = function () {
					var promise = Promise.prototype[method].apply(this, arguments);
					promise.__proto__ = this.__proto__; // FireFix
					return promise;
				};
			});
		

The End