Javascript.RU

Создать новую тему Ответ
 
Опции темы Искать в теме
  #1 (permalink)  
Старый 16.12.2016, 21:23
Аватар для Malleys
Профессор
Отправить личное сообщение для Malleys Посмотреть профиль Найти все сообщения от Malleys
 
Регистрация: 20.12.2009
Сообщений: 1,714

Как асинхронно вызвать resolve() вне Promise?
Всем привет! Понадобился Promise, который бы выполнялся, когда объект с данными создан. Необходимо, чтобы сторонний код "приостанавливал" выполнение моего скрипта, пока объект не будет готов.

Я считаю, что нет необходимости запихивать длинные куски кода в Promise только ради того, чтобы вызвать resolve() или reject().
Поэксперементировал с аксессорами...

var setter;
var promise = new Promise(function(resolve, reject) {
	setter = function(value) {
		if(value) {
			resolve();
		}
	};
});

Object.defineProperty(this, "objectCreated", {
	set: setter,
	get: function() {
		return promise;
	}
});

// много строк спустя...

this.objectCreated = true;


Hо пришлось отказаться от этой идеи, поскольку появляются лишние переменные. И выглядит это ужасно. Думаю, более элегантным (по аналогии с Promise.resolve()) решением проблемы является метод Promise.prototype.resolve(), который заставляет выполниться Promise.

Promise.prototype.resolve = function(value) {
	// как здесь вызвать resolve() ?
};
Ответить с цитированием
  #2 (permalink)  
Старый 17.12.2016, 00:14
Аспирант
Отправить личное сообщение для fuckingquest Посмотреть профиль Найти все сообщения от fuckingquest
 
Регистрация: 28.10.2016
Сообщений: 70

Сообщение от Malleys
который заставляет выполниться Promise.
А какой смысл его заставлять выполнится, если данные еще не готовы? Ведь промисы как раз и предназначены для упорядочивания асинхронных событий. Не совсем ясно, что Вы хотите. Ваш сторонний объект при готовности должен дернуть эту функцию, или что?
Ответить с цитированием
  #3 (permalink)  
Старый 17.12.2016, 00:30
Аспирант
Отправить личное сообщение для fuckingquest Посмотреть профиль Найти все сообщения от fuckingquest
 
Регистрация: 28.10.2016
Сообщений: 70

что же касается непосредственно вопроса:
Сообщение от Malleys
Как асинхронно вызвать resolve() вне Promise?
то это делается вот так, вроде:
promise = new Promise(function(resolve){
   setTimeout(function(){resolve(100)}, 1000)
})

anotherCode = function(){
  setTimeout(function(){
     Promise.resolve(promise).then(function(value){console.log(value)})
  }, 2000)
}

anotherCode()

хотя, по-сути, это, похоже, лишние костыли, можно и так это сделать:
promise = new Promise(function(resolve){
   setTimeout(function(){console.log("first run"); resolve(100)}, 1000)
})

anotherCode = function(){
  setTimeout(function(){
     console.log("second run")
     promise.then(function(value){console.log(value)})
  }, 2000)
}

anotherCode()


То есть, как только Вы сеттите коллбек, условно говоря "onresolve", тут же resolve(точней -- его внутреннее представление) и вызывается(на уровне реализации), если данные готовы.

Последний раз редактировалось fuckingquest, 17.12.2016 в 01:15.
Ответить с цитированием
  #4 (permalink)  
Старый 17.12.2016, 11:28
Аватар для Malleys
Профессор
Отправить личное сообщение для Malleys Посмотреть профиль Найти все сообщения от Malleys
 
Регистрация: 20.12.2009
Сообщений: 1,714

Спасибо за мнение! К сожалению ваш код не решает проблему,
promise = new Promise(function(resolve){
	setTimeout(function(){console.log("first run"); resolve(100)}, 1000)
})
Здесь resolve(100) не может быть вызвано раньше, чем сработает setTimeout...



Сообщение от fuckingquest Посмотреть сообщение
А какой смысл его заставлять выполнится, если данные еще не готовы?
Они готовы, но находятся не внутри функций "промиса". Другой вариант -- заставить "промис" передумать, т. е. как вызвать reject() извне?

Простая обёртка вокруг каждого "промиса" позволяет добиться такого результата
function defer(promise) {
	var resolvingFunctions;
	var promise = new Promise(function(resolve, reject) {
		resolvingFunctions = { resolve, reject };
	});
	
	promise.resolve = resolvingFunctions.resolve;
	promise.reject = resolvingFunctions.reject;

	return promise;
}

// Пример
var promise = defer();
promise.then(function() {
	console.log("5+");
});

// "промис", выполнись!
promise.resolve();


Однако мне не нравится отдельный объект(который впринципе не нужен) и то, что придётся оборачивать "промисы", поэтому вопрос о том, как вызвать resolve() остаётся актуальным.
Т. е. как написать resolve/reject в Promise.prototype, я не представляю... Проблема в том, что нет доступа к resolvingFunctions из 8 пункта.

When the Promise function is called with argument executor the following steps are taken:
1. If NewTarget is undefined, throw a TypeError exception.
2. If IsCallable(executor) is false, throw a TypeError exception.
3. Let promise be OrdinaryCreateFromConstructor(NewTarget, "%PromisePrototype%", «‍[[PromiseState]], [[PromiseResult]], [[PromiseFulfillReactions]], [[PromiseRejectReactions]]» ).
4. ReturnIfAbrupt(promise).
5. Set promise's [[PromiseState]] internal slot to "pending".
6. Set promise's [[PromiseFulfillReactions]] internal slot to a new empty List.
7. Set promise's [[PromiseRejectReactions]] internal slot to a new empty List.'
8. Let resolvingFunctions be CreateResolvingFunctions(promise).
9. Let completion be Call(executor, undefined, «resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]]»).
10. If completion is an abrupt completion, then
	a. Let status be Call(resolvingFunctions.[[Reject]], undefined, «completion.[[value]]»).
	b. ReturnIfAbrupt(status).
11. Return promise.

(Цитата из спецификации ECMA http://www.ecma-international.org/ec...omise-executor)

Эта цитата навеяла меня на то, что если resolvingFunctions в 9 пункте используются в функций, к которой есть доступ, то можно переписать конструктор "промиса"
(function(root, nativePromise) {
	function Promise(executor) {
		var resolvingFunctions;
		var promise = new nativePromise(function(resolve, reject) {
			resolvingFunctions = { resolve, reject };

			executor.call(undefined, resolve, reject);
		});

		promise.resolve = resolvingFunctions.resolve;
		promise.reject = resolvingFunctions.reject;

		return promise;
	}

	// это потребовалось, поскольку переопределён конструктор
	// и следовательно статичные методы улетели, возвращаем на место
	"race reject resolve".split(" ").forEach(function(method) {
		Promise[method] = nativePromise[method];
	});

	// теперь Promise.resolve() instanceof Promise === false
	// поскольку nativePromise !== Promise, исправляем
	Object.defineProperty(Promise, Symbol.hasInstance, {
		value: function(instance) {
			return instance instanceof nativePromise;
		}
	})

	Promise.prototype.constructor = Promise;

	root.Promise = Promise;
})(this, Promise);


Можно сказать проблема решена, если кто более гениально не придумает!

Впринципе теперь можно легко создать промис, который может передумать!
var promise = new Promise(function(resolve) {
	setTimeout(resolve, 2500);
});

promise.then(function() {
	alert("Свершилось!");
});

// Если вызвать эту функцию раньше чем выполнится "промис",
// то он никогда не выполнится
function nowIThinkAnotherWay() {
	promise.reject();
}


Рабочии пример http://codepen.io/Malleys/pen/VmqdeN?editors=0010
Ответить с цитированием
  #5 (permalink)  
Старый 17.12.2016, 15:32
Аватар для destus
Профессор
Отправить личное сообщение для destus Посмотреть профиль Найти все сообщения от destus
 
Регистрация: 18.05.2011
Сообщений: 1,207

Malleys,
Цитата:
Можно сказать проблема решена
Для отмены XHR запросов работать не будет.

Как вариант, можно сделать через потоки, чтобы не устраивать колбасу с переписыванием функционала реализации промиса в браузере.
<script src='https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.0.1/Rx.js'></script>
<button id='b1'>start</button>
<button id='b2'>cancel</button>
<script>
let [b1, b2] = document.querySelectorAll('button'),
	o = Rx.Observable.fromEvent(b1, 'click')
						 .switchMap(() => Rx.Observable.timer(2000).takeUntil(Rx.Observable.fromEvent(b2, 'click')))				 
					 
o.subscribe(() => document.body.style.backgroundColor = "#" + ("000000" + (Math.random() * 0xffffff | 0).toString(16)).slice(-6))
</script>

Последний раз редактировалось destus, 17.12.2016 в 15:35.
Ответить с цитированием
  #6 (permalink)  
Старый 18.12.2016, 00:09
Аспирант
Отправить личное сообщение для fuckingquest Посмотреть профиль Найти все сообщения от fuckingquest
 
Регистрация: 28.10.2016
Сообщений: 70

Сообщение от Malleys
"промис", выполнись!
У Вас там нет никакого асинхронного кода, это бессмысленная затея.
Ответить с цитированием
  #7 (permalink)  
Старый 18.12.2016, 06:50
Аватар для Malleys
Профессор
Отправить личное сообщение для Malleys Посмотреть профиль Найти все сообщения от Malleys
 
Регистрация: 20.12.2009
Сообщений: 1,714

Сообщение от fuckingquest Посмотреть сообщение
У Вас там нет никакого асинхронного кода, это бессмысленная затея.
Ну я и написал про тот defer(), что
Сообщение от Malleys Посмотреть сообщение
мне не нравится отдельный объект(который впринципе не нужен)
Ответить с цитированием
  #8 (permalink)  
Старый 18.12.2016, 07:16
Аватар для Malleys
Профессор
Отправить личное сообщение для Malleys Посмотреть профиль Найти все сообщения от Malleys
 
Регистрация: 20.12.2009
Сообщений: 1,714

Сообщение от destus Посмотреть сообщение
Malleys, для отмены XHR запросов работать не будет.
Да, поскольку Promise.prototype.reject === undefined
Мой способ добавляет reject во время создания экземпляра "промиса".

(function(root, nativeFetch) {
	function fetch() {
		var args = arguments;
		return new Promise(function(resolve, reject) {
			nativeFetch.apply(null, args).then(resolve, reject);
		});
	}

	root.fetch = fetch;
})(this, fetch);


Сообщение от destus Посмотреть сообщение
<script src='https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.0.1/Rx.js'></script>
Я не особый любитель больших библиотек. Rx.js содержит более 14000 строк кода и он избыточен для задачи, которая может быть решена в 39 строк кода.
Ответить с цитированием
  #9 (permalink)  
Старый 18.12.2016, 08:00
Аватар для destus
Профессор
Отправить личное сообщение для destus Посмотреть профиль Найти все сообщения от destus
 
Регистрация: 18.05.2011
Сообщений: 1,207

Malleys,
Rx.js это модульная библиотека, как и Lodash. Не нужно подключать её целиком, если скажем в скриптах используется Observable как сущность и 3-4 оператора. Достаточно подключить только нужное. А задачи, решаемые через промисы, можно решать и потоками, только делать это более качественно.

Вот так например выглядит автокомплит с википедии.
import { Subject }  from 'rxjs/Subject';

private searchTermStream = new Subject<string>();

searchTermStream
      .debounceTime(300)
      .distinctUntilChanged()
      .switchMap((term: string) => this.wikipediaService.search(term));

search(term: string) { this.searchTermStream.next(term); }
Ответить с цитированием
Ответ



Опции темы Искать в теме
Искать в теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Как вызвать метод? ibolgenos Общие вопросы Javascript 22 20.09.2014 00:04
Как вызвать функцию из переменной? sss2019 Общие вопросы Javascript 7 02.09.2014 13:34
как вызвать анонимную функцию которая находится в подключенном файле(.js) arsen97 Общие вопросы Javascript 3 03.08.2014 00:23
Как вызвать свою функцию из «чужого» кода в Java Script, не переписывая «чужой» код? korobochkin Библиотеки/Тулкиты/Фреймворки 2 19.07.2014 16:17
Как вызвать событие после события определенного в onchange Наталья Events/DOM/Window 2 12.09.2009 13:51