Pure JavaScript $.Deferred - javascript

How would I write the following without jQuery?
var dfd = $.Deferred()
dfd.done(done)
dfd.resolve()
function done() {
console.log('done')
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Use native promises:
Promise
.resolve()
.then(done);
function done() {
console.log('done')
}
Update
Without the chain:
let op = Promise.resolve();
op.then(done);
function done() {
console.log('done')
}

function Deferred (){
let res,rej,p = new Promise((a,b)=>(res = a, rej = b));
p.resolve = res;
p.reject = rej;
return p;
}
You just need to expose resolve and reject to make it work.

The problem with using native promises is that the resolve and reject handler is provided in the callback, so if you try and call them before they are actually assigned.
In my opinion it's more robust to just implement a deferred yourself for example:
function deferred() {
let thens = []
let catches = []
let status
let resolvedValue
let rejectedError
return {
resolve: value => {
status = 'resolved'
resolvedValue = value
thens.forEach(t => t(value))
thens = [] // Avoid memleaks.
},
reject: error => {
status = 'rejected'
rejectedError = error
catches.forEach(c => c(error))
catches = [] // Avoid memleaks.
},
then: cb => {
if (status === 'resolved') {
cb(resolvedValue)
} else {
thens.unshift(cb)
}
},
catch: cb => {
if (status === 'rejected') {
cb(rejectedError)
} else {
catches.unshift(cb)
}
},
}
}
const d = deferred()
setTimeout(() => {
d.resolve('good')
}, 1000)
// Will be called after 1s
d.then(value => console.log('#1 resolved!', value))
setTimeout(() => {
// Will be called after 3s and executed right away as it's already resolved
d.then(value => console.log('#2 resolved!', value))
}, 3000)

Related

Turning RxJS Observable into an asynchronous iterable

Sometimes I'd like to use the RxJS operators to manipulate an endless asynchronous iterable without buffering the values. It is easy to turn an iterable into an Observable. Are there downsides in the following approach to turn an Observable into an asynchronous iterable?
const iterable = async function* (observable) {
let buffer = [],
resolve,
reject;
const subscription = observable.subscribe({
next: value => {
if (resolve) {
resolve(value);
resolve = reject = undefined;
} else {
buffer.push(Promise.resolve(value));
}
},
error: e => {
if (reject) {
reject(e);
resolve = reject = undefined;
}
},
complete: () => {},
});
while (!subscription.isStopped || buffer.length) {
yield buffer.shift() ||
new Promise((_resolve, _reject) => {
resolve = _resolve;
reject = _reject;
});
}
subscription.unsubscribe();
};
Codepen demo
Here is an alternative implementation of the iterable as an Observer (without using a generator).
class IterableObserver {
constructor(observable) {
this.buffer = [];
this.resolve = undefined;
this.reject = undefined;
this.isStopped = false;
observable && observable.subscribe(this);
}
[Symbol.asyncIterator]() {
const t = this;
return {
next: async () => {
if (!t.isStopped || t.buffer.length) {
if (t.buffer.length) {
return {
value: t.buffer.shift(),
};
} else {
return new Promise((_resolve, _reject) => {
t.resolve = _resolve;
t.reject = _reject;
});
}
} else {
return { done: true };
}
},
};
}
next(value) {
if (this.resolve) {
this.resolve({ value });
this.resolve = this.reject = undefined;
} else {
this.buffer.push(value);
}
}
error(e) {
this.isStopped = true;
if (this.reject) {
this.reject(e);
this.resolve = this.reject = undefined;
}
}
complete() {
this.isStopped = true;
}
}
The benefit from this was questioned. Let's say you have an API which provides you with an asynchronous iterable of text file lines through the function makeTextFileLineIterator and you would need to provide an asynchronous iterable of the first N lines in uppercase for your client. How would you do that? With the RxJS operators and the iterable conversion it would be easy:
const lineIterator = makeTextFileLineIterator('https://cdn.skypack.dev/rxjs#^7.1.0/operators?min');
const upperCaseLines = new IterableObserver(from(lineIterator).pipe(
take(6),
map(line=>line.toLocaleUpperCase())),
);
Codepen demo

How to set a JS variable depending on the duration of an asynchronous call

I have this:
this.toggleWaiting()
this.results = await this.query(term)
this.toggleWaiting()
First a loading spinner gets triggered.
Then the query runs.
And when the query function ended the loading spinner gets closed.
But what if I want to just show the loading spinner, when the query takes maybe more then 0.5 seconds?
Is there a simple way to do this?
A way to achieve this is to pass the this.query(term) promise to a function which will handle triggering the toggleWaiting only when the query takes longer than the specified amount of time (using the timeout).
For example, the below takes a promise, a function (waitingFn) which will be called with the isWaiting status as well as a timeout which you can use to specify how long you want to wait before you show the loading spinner. Finally, when the promise has been fulfilled, we return the result:
async function handleWaiting(promise, waitingFn, timeout) {
let loadingStarted = false;
let timeoutInstance = null;
const timeoutPromise = new Promise((res) => {
timeoutInstance = setTimeout(() => {
loadingStarted = true;
waitingFn(true);
}, timeout);
return res();
});
function onFinished() {
clearTimeout(timeoutInstance);
if (loadingStarted) {
waitingFn(false);
}
}
try {
const [result] = await Promise.all([promise, timeoutPromise]);
onFinished();
return result;
} catch (ex) {
onFinished();
throw ex;
}
}
You can call the handleWaiting function like so:
const result = await handleWaiting(this.query(term), (isWaiting) => this.toggleWaiting(), 500);
As #FZs and #Bergi have pointed out (thank you both), the below is an antipattern due to using the promise constructor:
function handleWaiting(promise, waitingFn, timeout) {
return new Promise((res, rej) => {
let loadingStarted = false;
const timeoutInstance = setTimeout(() => {
loadingStarted = true;
waitingFn(true);
}, timeout);
function onFinished() {
if (loadingStarted) {
waitingFn(false);
}
clearTimeout(timeoutInstance);
}
return promise
.then((result) => {
onFinished();
res(result);
})
.catch((ex) => {
onFinished();
rej(ex);
});
});
}
Thanks to ljbc1994 I found a nice solution.
Inside my alpineJs object - I have this implementation:
{
waiting: false,
async handleWaiting(promise, timeout) {
return new Promise((res, rej) => {
let loadingStarted = false;
const timeoutInstance = setTimeout(() => {
loadingStarted = true;
this.waiting = true;
}, timeout);
const onFinished = () => {
if (loadingStarted) {
this.waiting = false;
}
clearTimeout(timeoutInstance);
}
promise
.then((result) => {
onFinished();
res(result);
})
.catch((ex) => {
onFinished();
rej(ex);
});
});
},
async searchForTerm(term) {
this.results = await this.handleWaiting(this.$wire.query(term), 500);
// do something with the results...
},
}
Pretty straightforward.
For someone who is interested in the full code - here is the commit inside the github repo:
https://github.com/MichaelBrauner/sunfire-form/commit/7c1f8270e107a97b03264f5ddc5c3c3ae6f7cfd7

Resolve promise after callback it's executed

I have the following code on which I am trying to block the execution of the method _saveAddress multiple time, so I made a promise for this method.
const [pressEventDisabled, setPressEventDisabled] = useState(false);
<TouchableOpacity style={style.button_container} activeOpacity={1} disabled={pressEventDisabled} onPress={async () => {setPressEventDisabled(true); await _saveAddress(); setPressEventDisabled(false);}} >
The problem is that I want to resolve the promise after the callback method it's executed. It's there any way to wait for the dispatch function to execute or to resolve the promise inside the callback method?
This is the method for saving the address:
const _saveAddress = () => new Promise(async (resolve) => {
var valid = _validate();
if (valid) {
const address = createAddressJson();
if (addressId) {
var addressIdProperty = {
id: addressId
};
const newAddress = Object.assign(addressIdProperty, address);
dispatch(editAddress(newAddress, _onAddressSaveEditCallback));
} else {
dispatch(addAddress(address, _onAddressSaveEditCallback));
}
} else {
//notify
notifyMessage(strings.fill_required_inputs_validation);
resolve();
}
});
This is the callback method:
const _onAddressSaveEditCallback = async (success: boolean, apiValidations: any, address ? : Address, ) => {
if (success) {
if (typeof callback == 'function') {
callback(address);
}
await Navigation.pop(componentId);
} else {
setDataValidations(apiValidations);
}
};
Just do exactly what you say in the title. Nothing more, nothing less:
if (addressId) {
var addressIdProperty = {id: addressId};
const newAddress = Object.assign(addressIdProperty, address);
dispatch(editAddress(newAddress, async (s,v,a) => {
await _onAddressSaveEditCallback(s,v,a);
resolve();
}));
} else {
dispatch(addAddress(address, async (s,v,a) => {
await _onAddressSaveEditCallback(s,v,a);
resolve();
}));
}
Of course, since you are passing async () => {} to addAddress instead of _onAddressSaveEditCallback you have to call _onAddressSaveEditCallback yourself since addAddress will be calling the async () => ...
But mixing promises and callbacks like this isn't great. It leads to weird looking and sometimes confusing code. A better solution is to promisify addAddress:
function addAddressPromise (address) {
return new Promise((resolve, reject) => {
addAddress(address, (success, validations, address) {
if (success) return resolve(address);
else reject(validations)
});
});
}
Now you can wait for addAddress:
const _saveAddress = async () => {
// Don't create new promise here, we do it in addAddress..
// ...
let result = await addAddressPromise(address);
dispatch(result);
await _onAddressSaveEditCallback();
// ...
}

Javascript async function basic

First of all I am new to async functions. Where is the problem in this code? I just get test 1 and test 2 output.
let isAvailable = false;
console.log('test1');
let asynchFunc = () => {
return new Promise(() => {
if (isAvailable === true) {
console.log('here asyncFunc');
}
});
}
(async () => {
await asynchFunc();
});
console.log('test2');
isAvailable = true;
I don't know if i say it in a better way:
If you add a timeout inside the promise, isAvailable = true instruction will get executed before reaching the check on this variable, otherwise, isAvailable will remain false.
You can test both cases below
Without timeout
let isAvailable = false;
console.log('test1');
let asynchFunc = () => {
return new Promise((resolve, reject) => {
if (isAvailable === true) {
console.log('here asyncFunc');
resolve('has been available');
} else {
reject('still not available')
}
});
}
(async () => {
try {
let res = await asynchFunc();
console.log(res);
} catch (e) {
console.log(e);
}
})();
console.log('test2');
isAvailable = true;
With Timeout
let isAvailable = false;
console.log('test1');
let asynchFunc = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (isAvailable === true) {
console.log('here asyncFunc');
resolve('has been available');
} else {
reject('still not available')
}
}, 2000);
});
}
(async () => {
try {
let res = await asynchFunc();
console.log(res);
} catch (e) {
console.log(e);
}
})();
console.log('test2');
isAvailable = true;
Did you mean to call the async function like this?
(async () => {
await asynchFunc();
})();
(Note the added () at the end)
I'm not sure what you're trying to test here, the async function will still see the false value. You can see that by changing your function to:
let asynchFunc = () => {
return new Promise(() => {
console.log('check isavailable:', isAvailable);
});
}
If you're just looking for a setup that will let you play around with async functions then you can use this code:
const sleep = (time) => new Promise((resolve) => setTimeout(resolve, time));
let isAvailable = false;
(async () => {
console.log('isAvailable:', isAvailable);
await sleep(1000);
console.log('isAvailable:', isAvailable);
})();
console.log('test2');
isAvailable = true;
Just be aware that using a sleep function like this to wait for something to be available is completely the wrong way of doing things. You should never have an isAvailable flag, the value should just be a promise that will resolve once it's available. What language are you coming from? What is your actual issue? We can help you better with real world problems.
Here's a function you can play around with that might help you understand what's going on a bit more. Note you're using a immediately invoked function expression (IIFE) to switch from synchronous to asynchronous code, which is fine as long as you understand that console.log('test2') in your original code may not process in the same order as your code looks like it might. However, you did not call the IIFE, so simply add () to the end of it.
I.e.,
(function (){})();
(()=>{})();
(async function(){})();
(async ()=>{})();
As for the rest of your code, you don't resolve or reject your promise, return the promise correctly from the function, and you need to try{}catch{} your asynchronous function otherwise reject() from the promise will be unhandled.
(function(){
let isAvailable = false;
let asynchFunc = () => new Promise((resolve,reject) => {
if (isAvailable === true) {
console.log('Available');
resolve(true);
}else{
console.log('Not available');
reject(false)
}
});
(async () => {
try{
// isAvailable = true; // test with me.
console.log('test1');
// result is always true, unless there is an error.
// but if there is an error
// the rejected promise will go to catch(e){}.
let result = await asynchFunc();
console.log('test2');
isAvailable = true;
}catch(e){
console.log('Error:',e)
}
})();
})();
let isAvailable = false;
console.log('test1');
let asyncFunc = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (isAvailable === true) {
resolve('here asyncFunc');
}
}, 2000);
});
}
(async() => {
return await asyncFunc(); // await used here is optional
})().then(v => {
console.log(v); // prints value after 2 seconds.
});
console.log('test2');
isAvailable = true;
All Promises need to be either resolved or rejected. In your case you did neither. In order to see the value in the async function, I passed the value to the resolve callback.
In an async function it is optional to use the await key when returning a value as the return value of an async function is implicitly wrapped in Promise.resolve.

Fluent async api with ES6 proxy javascript

So... I have some methods. Each method returns a promise.
myAsyncMethods: {
myNavigate () {
// Imagine this is returning a webdriverio promise
return new Promise(function(resolve){
setTimeout(resolve, 1000);
})
},
myClick () {
// Imagine this is returning a webdriverio promise
return new Promise(function(resolve){
setTimeout(resolve, 2000);
})
}
}
I'm trying to make end to end tests, so the prom chain must be linear (first click, next navigate, etc)
For now, I can do this...
makeItFluent(myAsyncMethods)
.myNavigate()
.myClick()
.then(() => myAsyncMethods.otherMethod())
.then(() => /*do other stuff*/ )
...with ES6 proxy feature:
function makeItFluent (actions) {
let prom = Promise.resolve();
const builder = new Proxy(actions, {
get (target, propKey) {
const origMethod = target[propKey];
return function continueBuilding (...args) {
// keep chaining promises
prom = prom.then(() => (typeof origMethod === 'function') && origMethod(...args));
// return an augmented promise with proxied object
return Object.assign(prom, builder);
};
}
});
return builder;
};
But, the thing I cannot do is the following:
makeItFluent(myAsyncMethods)
.myNavigate()
.myClick()
.then(() => myAsyncMethods.otherMethod())
.then(() => /*do other stuff*/ )
.myNavigate()
Because then is not a proxied method, and thus it does not return myAsyncMethods. I tried to proxy then but with no results.
Any idea?
thanks devs ;)
I would return wrapped Promises from yourAsyncMethods which allows mixing of sync and async methods with Proxy and Reflect and executing them in the correct order :
/* WRAP PROMISE */
let handlers;
const wrap = function (target) {
if (typeof target === 'object' && target && typeof target.then === 'function') {
// The target needs to be stored internally as a function, so that it can use
// the `apply` and `construct` handlers.
var targetFunc = function () { return target; };
targetFunc._promise_chain_cache = Object.create(null);
return new Proxy(targetFunc, handlers);
}
return target;
};
// original was written in TS > 2.5, you might need a polyfill :
if (typeof Reflect === 'undefined') {
require('harmony-reflect');
}
handlers = {
get: function (target, property) {
if (property === 'inspect') {
return function () { return '[chainable Promise]'; };
}
if (property === '_raw') {
return target();
}
if (typeof property === 'symbol') {
return target()[property];
}
// If the Promise itself has the property ('then', 'catch', etc.), return the
// property itself, bound to the target.
// However, wrap the result of calling this function.
// This allows wrappedPromise.then(something) to also be wrapped.
if (property in target()) {
const isFn = typeof target()[property] === 'function';
if (property !== 'constructor' && !property.startsWith('_') && isFn) {
return function () {
return wrap(target()[property].apply(target(), arguments));
};
}
return target()[property];
}
// If the property has a value in the cache, use that value.
if (Object.prototype.hasOwnProperty.call(target._promise_chain_cache, property)) {
return target._promise_chain_cache[property];
}
// If the Promise library allows synchronous inspection (bluebird, etc.),
// ensure that properties of resolved
// Promises are also resolved immediately.
const isValueFn = typeof target().value === 'function';
if (target().isFulfilled && target().isFulfilled() && isValueFn) {
return wrap(target().constructor.resolve(target().value()[property]));
}
// Otherwise, return a promise for that property.
// Store it in the cache so that subsequent references to that property
// will return the same promise.
target._promise_chain_cache[property] = wrap(target().then(function (result) {
if (result && (typeof result === 'object' || typeof result === 'function')) {
return wrap(result[property]);
}
const _p = `"${property}" of "${result}".`;
throw new TypeError(`Promise chain rejection: Cannot read property ${_p}`);
}));
return target._promise_chain_cache[property];
},
apply: function (target, thisArg, args) {
// If the wrapped Promise is called, return a Promise that calls the result
return wrap(target().constructor.all([target(), thisArg]).then(function (results) {
if (typeof results[0] === 'function') {
return wrap(Reflect.apply(results[0], results[1], args));
}
throw new TypeError(`Promise chain rejection: Attempted to call ${results[0]}` +
' which is not a function.');
}));
},
construct: function (target, args) {
return wrap(target().then(function (result) {
return wrap(Reflect.construct(result, args));
}));
}
};
// Make sure all other references to the proxied object refer to the promise itself,
// not the function wrapping it
Object.getOwnPropertyNames(Reflect).forEach(function (handler) {
handlers[handler] = handlers[handler] || function (target, arg1, arg2, arg3) {
return Reflect[handler](target(), arg1, arg2, arg3);
};
});
You would use it with your methods like
myAsyncMethods: {
myNavigate () {
// Imagine this is returning a webdriverio promise
var myPromise = new Promise(function(resolve){
setTimeout(resolve, 1000);
});
return wrap(myPromise)
},
// ...
Please note two things :
You might need a polyfill for Reflect : https://www.npmjs.com/package/harmony-reflect
We need to check proxy get handlers for built-in Symbols, e.g. : https://github.com/nodejs/node/issues/10731 (but also some browsers)
You can now mix it like
FOO.myNavigate().mySyncPropertyOrGetter.myClick().mySyncMethod().myNavigate() ...
https://michaelzanggl.com/articles/end-of-chain/
A promise is nothing more than a "thenable" (an object with a then() method), which conforms to the specs. And await is simply a wrapper around promises to provide cleaner, concise syntax.
class NiceClass {
promises = [];
doOne = () => {
this.promises.push(new Promise((resolve, reject) => {
this.one = 1;
resolve();
}));
return this;
}
doTwo = () => {
this.promises.push(new Promise((resolve, reject) => {
this.two = 2;
resolve();
}));
return this;
}
async then(resolve, reject) {
let results = await Promise.all(this.promises);
resolve(results);
}
build = () => {
return Promise.all(this.promises)
}
}
Them you can call it in both ways.
(async () => {
try {
let nice = new NiceClass();
let result = await nice
.doOne()
.doTwo();
console.log(nice);
let nice2 = new NiceClass();
let result2 = await nice2
.doOne()
.doTwo()
.build();
console.log(nice2, result2);
} catch(error) {
console.log('Promise error', error);
}
})();

Categories

Resources