Как асинхронно вызвать 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, время: 11:27. |