How to determine whether the other then() methods can be omitted? - javascript

The following codes illustrate a form which allows optionally pasting the value from the clipboard before automatically submitting. It uses two then() methods. Please use Chrome to test it because Firefox does not support it yet.
const input = document.querySelector('input'),
submitButton = document.querySelector('button[type="submit"]'),
pasteAndSubmitButton = document.querySelector('button[type="button"]');
pasteAndSubmitButton.addEventListener('click', function ()
{
navigator.clipboard.readText()
.then(clipText => input.value = clipText)
.then(() => submitButton.click());
});
<form>
Address: <input type="text" name="address" required>
<button type="submit">Submit</button>
or
<button type="button">Paste and Submit</button>
</form>
I'm wondering whether it is safe to omit the second then() method and move the code inside into the first one like this:
navigator.clipboard.readText()
.then(clipText =>
{
input.value = clipText;
submitButton.click();
});
My test shows that the result is the same. But I'm just not sure whether it is always safe to omit the second then() method because the fetch examples I have seen so far use two then() methods.

Many fetch examples use two thens because two promises are involved: one to get the response header, and one to read the body. In other words, both fetch and typical follow-up methods like Response.text() return a promise. Two promises aren’t involved here, so you only need one then.

Related

Promise D3 js inside 'then'

Trying to understand the different steps to the Promise implementation for D3 js.
Have two input files and promise them all:
Promise.all([jsonFile, txtFile]).then(input)
The array has been consolidated into one individual input, which might be called as a one-parameter function like:
function input(data) {
console.log(data[0]) // json
console.log(data[1]) // txt
}
Imagine I want to implement the second function as a two-parameter with the two inputs like function input(json, txt). What should happen in the then() statement to make the code do so?
Thanks
If you want to implement input function with two parameters:
function input(json, txt) {
// omitted
}
then you can use rest parameters [more]:
Promise.all([jsonFile, txtFile])
.then((data) => input(...data))
or you can be more explicit:
Promise.all([jsonFile, txtFile])
.then(([json, txt]) => input(json, txt))
Your question is just about the Promise.all behavior.
The documentation states that you have right expectations and the order of promises is preserved when they are resolved:
Returned values will be in order of the Promises passed, regardless of completion order.
const promises = [Promise.resolve(1), Promise.resolve('some text')]
const processInputCallback = console.log
Promise.all(promises).then(processInputCallback)
Maybe the only thing to add here is that if any of promises gets rejected it will fail without waiting for all promises to get resolved.

Problem with reading data from Cache API (still in promise?)

That is my first post here. I am not well skilled in asynchronous code so can not resolve the problem by myself
In a React/Redux app I have added cache. The idea behind it is to have something like 'Favorites' functionality but on clients' computer. So, I would like to store over there some data about books. While writing to cache works I can not successively dispatch data to store> Now my code looks like this:
export function fetchFromFavorites() {
return async (dispatch, getState) => {
const URL = getState().books.currentURL;
const Cache = await caches.open(URL);
Cache.matchAll()
.then(function (response) {
const ar = [];
response.forEach(async item => ar.push(await item.json()));
return ar;
})
.then(response => dispatch(test(response)));
};
}
In the code above test is an action that only sets the state field with payload. While the payload can be log-consoled from reducer, I can not perform on that any further action with another external function, well-checked on that kind of data. Besides DevTools mark it with blue 'i' what indicates that it has been calculated very lately. What is wrong with that code? BTW - it has nothing to do with service workers it is just inside regular React.
The function you are passing to response.forEach is returning a promise. You'd need to wait for all of those promises to resolve before returning ar.
For example, you may use something like:
// await all promises to be fulfilled before proceeding.
// note that we use response.map instead of forEach as
// we want to retain a reference to the promise returned
// by the callback.
// Additionally, we can just return the promise returned
// by `item.json()`
await Promise.all(response.map(item => item.json());
Remember, any function marked as async will return a promise wrapping the function's return type.
Note that you're mixing async/await with older style then/catch promises here. For consistency and ease of reading, you may want to use one style consistently.

React.js set callback function after execution of first function

I have been calling an api on onClick event of my React.js/Redux app. After calling the api, I have to call another api based on the results of first API. So I was thinking about async calls.
My code:
<a href="" onClick={(e) => this.sendData(e)} >Send</a>
and method sendData is
sendData(e){
e.preventDefault();
this.props.sendDataApi({this.refs.msg.value});
//After receiving results from above api, i have to call another api async
this.props.getDetails(this.refs.txt.value);
}
But currenlty both APIs called at the same time. Any help!
You should consider using async/await pattern which handles that scenario well. Your code would then look like this:
const sendData = async message => {
const messageId = await sendDataApi(message)
const details = await getDetails(messageId)
return details
}
However, if you are not able to use newest language features you may use Promises. Keep in mind that the code will be more prone to callback-hell and that they might also need polyfilling.
The same code with promises would look like this:
const sendData = message => {
sendDataApi(message)
.then(messageId => getDetails(messageId))
}
This code is a mere example of the issues with Promises because it can be reduced just to sendDataApi(message).then(getDetails). Problems arise when there are complex conditional logic, error handling, more tasks, and parameter passing.
React handles asynchrony very poorly so do the Redux. You need to use redux-thunk, redux-saga or another middleware. With that, you will be able to dispatch another action from the action creator (continuation, trunk, saga - you name it). It can become tedious, require much boilerplate, and be very difficult to debug. You may consider leaving Redux synchronous state management and consider other solutions (RxJS maybe?) for those kinds of problems.
Please see an example of using Promises to chain two asynchronous calls using React's default setState (see console output).
With redux-thunk the implementation might look like this (other actions not show, no error handling etc.):
...
const sendData = message => {
return dispatch => {
return sendDataApi(message).then(
messageId => dispatch(getDetails(messageId)),
);
};
}

Are Promises A+ promises required to execute side effects AFTER `.then` is called?

I got tripped up by how the Knex API for building a schema doesn't actually create a table until you call .then.
For example, this code won't actually affect the database:
knex.schema.createTable('users', table => {
table.string('name')
})
But this code will:
knex.schema.createTable('users', table => {
table.string('name')
}).then(console.log.bind(console))
Is this behavior (not doing anything until .then is called):
a) required by the Promises A+ spec
b) prohibited by the Promises A+ spec
c) unspecified
?
I read the spec, and it seems like the behavior is unspecified, but I'm not sure. This seems too important to be unspecified.
Update
see #Vohuman's answer: The then method on the Knex schema builder first performs a side effect, then returns a promise. So even if the answer to my question is (b) Knex wouldn't be in violation of the spec. Though the choice of then as a method name is very misleading in this case.
This isn't exactly "wrong", though it isn't common. Knex requires .then because it has to be able to tell when the query is done, as opposed to mid-build.
Example from the docs (commentary added):
knex.select('name').from('users')
.where('id', '>', 20) // Can I issue the query yet?
.andWhere('id', '<', 200) // Okay I'll...oh, you have more conditions.
.limit(10) // Hey, I'm here, still ready...
.offset(x) // Just let me know when you want me to--
.then(function(rows) { // OH OKAY I'll issue it now.
return _.pluck(rows, 'name');
})
Google's API.js helper follows this pattern too, specifically for immediate vs batched queries:
When you create a request with the intention of adding it to a batch, do not invoke its then method until after the request has been added to the batch. If the then method is invoked before the request is added, the request is sent immediately instead of as part of the batch.
As SLaks pointed out, this is not explicitly specified anywhere in the docs: In order for the object to be Promises/A+ compliant, it must have a method named then with the proper semantics and return value, and nothing specifies that then cannot have additional behavior. Naturally this prohibits these API libraries from renaming the then method to something more apt, like thenIssueRequest or thenGetResponse. (You can add an alias, but then is required to exist.)
As an API designer, the only alternative would be to separate the creation of the promise with the chaining of then, with the caveat that almost every call to then would have an extra method call that precedes or wraps it. Because then is the only way to access the result, I can understand how optimizing for the common case would result in removing the extra method.
fluentApi.createRequest().parameterA(1).parameterB(2).issue().then(...);
// or
fluentApi.issue(fluentApi.createRequest().parameterA(1).parameterB(2)).then(...);
Finally, bear in mind that you should always catch a Promise at some point, which would trigger the request:
knex.schema.createTable('users', table => {
table.string('name')
}).catch(console.log.bind(console));
...and that both arguments to then are optional, so if you insist on skipping error handling you can even have an empty then:
knex.schema.createTable('users', table => {
table.string('name')
}).then(null);
knex.schema.createTable('users', table => {
table.string('name')
}).then();
This behavior is wrong; calling then() should not have any side-effects beyond the promise itself (i.e., executing the callback).
However, the spec doesn't actually say anything about this.
knex is a query builder. then is just a helper function that executes the generated [SQL] statements and calls the then function of a promise object behind the scenes. If you replace the then with the .toString function you will get the generated string. then here is not a method of a promise object.
From knex source code:
Target.prototype.then = function(/* onFulfilled, onRejected */) {
const result = this.client.runner(this).run()
return result.then.apply(result, arguments);
};

Does Angular have a predefined object that houses all current promises?

I have a controller where I can allow everything except one function ( let's call it thatOneFunction() ) to run asynchronously. The reason for that is thatOneFunction() relies on roughly 10 promises to have been resolved otherwise it does not have the data in the scope it needs to execute.
I have seen multiple examples online of how you can specifically grab the promise of individual gets/queries/services but what I have not seen is a way to check that all promises at once have been solved - perhaps by virtue of an Angular state variable that I am just not aware of? Something like:
var currentPromises = $state.promises;
currentPromises.then(thatOneFunction());
Or if not already a built-in variable is there a way to dynamically generate a list of all promises outstanding at the current moment that I can then use .then()? It would assist a lot because I will have many pages in this application that have this structure.
What I have tried/am currently doing"
As of right now I have a work around but it requires some hardcoding that I'd rather not continue. It is based on inserting a call to a function that checks a counter of how many times that function has been called, compares it to how many promises should have called that function already (hardcoded after counting the the promises in the controller) and if it has already gone through all the required promises it then calls thatOneFunction().
Thanks for any help/pointers.
Example bits of my code (real names/variables changed):
Controller
$scope.runAfterAllPromises = function() {
*code dependedent on all promises here*
}
MySvc.loadList(function (results) {
$scope.myList = results;
});
$scope.runAfterAllPromises();
Service
app.service("MySvc", ['$resource', function ($resource) {
this.loadList = function (callBackFunction) {
var stateRes = $resource('/api/ListApi');
stateRes.query(function (ListResults) {
if (callBackFunction && typeof (callBackFunction) === 'function') {
callBackFunction(ListResults)
};
})
You can chain promises, if that helps?
so something like this:
var promises = [];
promises.push(yourPromise1());
promises.push(yourPromise2());
promises.push(yourPromise3());
$q.all(promises).then(function(){ ...});
This what you mean?
Short answer: No, no such object exists.
Now, how to achieve what you want to achieve:
var allPromise = $q.all([promise1, promise2, promiseN]);
allPromise.then(success).catch(error);
Edit: After your "A man can dream comment" I thought to add this:
You could override the $q.defer function, and track all deferred objects created, and get their promises.
Obviously this could have some issues with deferreds created, but never resolved and such, but may be the simplest way to achieve your desired functionality. If you want to go the whole way, you can look at angular decorators which provide a framework of functionality for extending angular providers

Categories