Как асинхронно вызвать 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() ?
};
|
Цитата:
|
что же касается непосредственно вопроса:
Цитата:
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(точней -- его внутреннее представление) и вызывается(на уровне реализации), если данные готовы. |
Спасибо за мнение! К сожалению ваш код не решает проблему,
promise = new Promise(function(resolve){
setTimeout(function(){console.log("first run"); resolve(100)}, 1000)
})
Здесь resolve(100) не может быть вызвано раньше, чем сработает setTimeout...Цитата:
Простая обёртка вокруг каждого "промиса" позволяет добиться такого результата
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 |
Malleys,
Цитата:
Как вариант, можно сделать через потоки, чтобы не устраивать колбасу с переписыванием функционала реализации промиса в браузере.
<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>
|
Цитата:
|
Цитата:
Цитата:
|
Цитата:
Мой способ добавляет 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);
Цитата:
|
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); }
|
| Часовой пояс GMT +3, время: 05:39. |