@ince

Есть ли человекочитаемый полифил промиса на коллбэках?

Мне для того, чтобы лучше понять устройство промиса
  • Вопрос задан
  • 935 просмотров
Решения вопроса 1
BRAGA96
@BRAGA96
Когда-то для себя писал, что бы самому понять как примерно работает под капотом. Думаю основной посыл будет понятен. Писал специально на es5.
promise.js
;(function () {
	'use strict';

	var cache = {};

	var promise = function (handler) {
		var uuid = Number(String(Math.random()).slice(2)).toString(32);

		cache[uuid] = {
			then: [],
			catch: []
		};

		var api = {
			status: {
				pending: true,
				resolved: false,
				rejected: false
			},

			queue: function (items, callback) {
				var ready = promise();
				var collection = [];
				(function next(index) {
					var item = callback(items[index], index, items);
					if (!item) return;
					item.then(function (data) {
						collection[index] = data;
						items[index + 1] ? next(index + 1) : ready.resolve(collection);
					});
				}(0));
				return ready;
			},

			parallel: function (items, callback) {
				var ready = promise();
				var collection = [];
				var counter = 0;
				for (var i = 0; i < items.length; i++) {
					(function (index) {
						var item = callback(items[index], index, items);
						if (!item) return;
						item.then(function (data) {
							counter++;
							collection[index] = data;
							if (items.length === counter) {
								ready.resolve(collection);
							}
						});
					}(i));
				}
				return ready;
			},

			all: function (promises) {
				var ready = promise();
				var collection = [];
				var counter = 0;
				for (var i = 0; i < promises.length; i++) {
					(function (index) {
						promises[index].then(function (data) {
							counter++;
							collection[index] = data;
							if (promises.length === counter) {
								ready.resolve(collection);
							}
						});
					}(i));
				}
				return ready;
			},

			then: function (callback, data) {
				if (api.status.resolved) {
					callback(data);
				} else {
					cache[uuid].then.push(callback);
				}
				return api;
			},

			catch: function (callback, data) {
				if (api.status.rejected) {
					callback(data);
				} else {
					cache[uuid].catch.push(callback);
				}
				return api;
			},

			resolve: function (data) {
				if (api.status.resolved) return api;
				api.status.pending = false;
				api.status.resolved = true;
				for (var i = 0; i < cache[uuid].then.length; i++) {
					api.then(cache[uuid].then[i], data);
				}
				return api;
			},

			reject: function (data) {
				if (api.status.rejected) return api;
				api.status.pending = false;
				api.status.rejected = true;
				for (var i = 0; i < cache[uuid].catch.length; i++) {
					api.catch(cache[uuid].catch[i], data);
				}
				return api;
			}
		};

		if (handler) {
			var instanse = promise();
			handler(instanse.resolve, instanse.reject);
			return instanse;
		} else {
			return api;
		}
	};

	window.promise = promise;

}());


И код примеров:
promise.examples.js
(function () {
	'use strict';

	/** Одиночный AJAX запрос */
	ajax('https://jsonplaceholder.typicode.com/posts/1').then(function (posts) {
		console.log(posts);
	});

	/** Паралельные AJAX запросы */
	promise().parallel([1, 2, 3, 4, 5, 6], function (id) {
		return ajax(`https://jsonplaceholder.typicode.com/posts/${id}`);
	}).then(function (posts) {
		console.log(posts);
	});

	/** Последовательные AJAX запросы */
	promise().queue([1, 2, 3, 4, 5, 6], function (id) {
		return ajax(`https://jsonplaceholder.typicode.com/posts/${id}`);
	}).then(function (posts) {
		console.log(posts);
	});

	/** Использование promise. Пример 1 - callback функция */
	getPostsMeta().then(function (posts) {
		console.log(posts);
	});

	function getPostsMeta() {
		return promise(function (resolve, reject) {
			ajax('https://jsonplaceholder.typicode.com/posts').then(function (posts) {
				var result = posts.map(function (post) {
					return {
						userId: post.userId,
						postId: post.id
					};
				});
				return resolve (result);
			}).catch(function (error) {
				return reject(error);
			});
		});
	}

	/** Использование promise. Пример 2 - создание инстанса */
	wait(800).then(function (time) {
		console.log(`Success async wait for ${time}ms`);
	});

	/** Версия с async/await */
	(async () => {
		const time = await wait(800);
		console.log(`Success async wait for ${time}ms`);
	})();

	function wait(time) {
		return promise(function (resolve) {
			setTimeout(function () {
				resolve(time);
			}, time);
		});
	}

	/** Async/Await с Thenable объектами */
	(async () => {
		var users = await ajax('https://jsonplaceholder.typicode.com/users');
		console.log(users);
	})();

	/** Callback выполнения всех промисов */
	var promises = [wait(500), wait(2500), wait(1000), wait(800)];
	promise().all(promises).then(function (timers) {
		console.log('All timers finished', timers);
	});

	// --------------------------------------------------------------------
	function ajax(link) {
		var ready = promise();
		var xhr = new XMLHttpRequest();
		xhr.onload = function () {
			if (xhr.status >= 200 & xhr.status < 400) {
				var data = tryCatch(function () {
					return JSON.parse(xhr.responseText);
				}, xhr.responseText);
				ready.resolve(data);
			} else {
				var error = {
					status: xhr.status,
					error: xhr.statusText
				};
				ready.reject(error);
			}
		};
		xhr.onerror = function () {
			var error = {
				status: xhr.status,
				error: xhr.statusText
			};
			ready.reject(error);
		};
		xhr.open('GET', link, true);
		xhr.send();
		return ready;
	}

	function tryCatch(done, fail) {
		try {
			if (typeof done === 'function') {
				return done();
			}
		} catch (error) {
			if (fail !== undefined) {
				if (typeof fail === 'function') {
					return fail(error);
				} else {
					return fail;
				}
			}
		}
	}

}());
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
pro_co_ru
@pro_co_ru
Старший инженер-программист
Обратите внимание на async/await.
Пока некоторые переходят с использования колбэков на промисы, другие переходят с промисов на async/await.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы
SummerWeb Ярославль
от 120 000 до 180 000 ₽
КРАФТТЕК Санкт-Петербург
от 60 000 до 80 000 ₽
Brightdata Тель-Авив
от 5 500 до 6 500 $
18 июн. 2024, в 23:10
15000 руб./за проект
18 июн. 2024, в 20:15
500 руб./за проект