I'm sure the answer to this question exists somewhere, but unsure how to phrase the question and don't find what I'm looking for when I try to research callbacks.
Ok, so I've just started dabbling with Gulp for compiling my CSS, and I think it's great!.. despite being useless with JavaScript.
My code is below, but I don't understand how these callbacks are working. How can callbacks be set as a parameter and then be called from inside the function? I don't get it.. is the function basically expecting something to be in there? What value is being set or what's expected to run? I can't seem to make sense of it.
I see this quite frequently in JavaScript, but unsure how it works. I've looked up videos and tutorials of functions and callbacks, they makes sense, but I never seem to be able to find anywhere where this concept is explained which makes me believe I'm not looking for the right thing.
I see the same sort of thing with Promises as well where 'resolve' or 'reject' parameters are set, but unsure what's going on when a condition is met, or what values are set and where.. hope this makes sense and appreciate any help in understanding this better.
const gulp = require('gulp');
const sass = require('gulp-sass');
const browserSync = require('browser-sync');
// TASKS
// Compile SASS
gulp.task('sass-compile', (callback) => {
gulp.src('scss/*.scss')
.pipe(sass().on('error', sass.logError))
.pipe(gulp.dest('css'))
.pipe(browserSync.stream());
console.log('******************** SCSS > CSS successful ********************');
callback();
});
// Live reload
gulp.task('browser-sync', (callback) => {
browserSync.init({
proxy: 'http://localhost/test1',
port: 80
});
callback();
});
// WATCHER
gulp.task('default', gulp.series('browser-sync', (callback) => {
gulp.watch('scss/*.scss', gulp.series('sass-compile'));
callback();
}));
function foo(baz) {
console.log("Foo: " + baz);
}
function other(bar) {
const arg = "Bar";
bar(arg);
}
other(foo);
I can see how this is useful when it comes to performing asynchronous tasks. But its quite counter intuitive and I can immediately see the potential for type errors.
I've since changed my coding style to use ASYNC/AWAIT instead as much as possible. But it still a pain to navigate legacy code. Hence, the reason why I ended up looking up this thread. Haha
Functions are a type of object. Objects are a type of value.
Values can be stored in variables, in object properties, and passed as arguments to functions.
So:
Here is a simple function, which is called.
function foo() {
console.log("Foo");
}
foo();
Here is a simple function, copied to a different variable, which is called.
function foo() {
console.log("Foo");
}
const bar = foo;
bar();
And now passed as an argument to another function.
function foo() {
console.log("Foo");
}
function other(bar) {
bar();
}
other(foo);
And the same, but with arguments passed to the original function.
function foo(baz) {
console.log("Foo: " + baz);
}
function other(bar) {
const arg = "Bar";
bar(arg);
}
other(foo);
You've just been looking at examples where the function responsible for calling the callback is not code you've written (and you aren't looking at the source code of the function which calls the callback).
In javascript, functions are first class objects, which means they can be passed around to other functions. There are many cases where this can be used, but the cases you're referring to (gulp.task and new Promise) are essentially using it to do two-way communication between your code and their code. When you use gulp.task, basically the following steps are happening
1) You call gulp.task, saying "i'd like to do some work please". You pass in a function saying what you'd like to do
2) at the appropriate time, gulp.task calls your function, but passes in to it another function saying "when you're done, call this to let me know".
A simplified version of gulp.task would look like this:
const task = (name, workToDo) => {
const done = () => {
console.log('task', name, 'has let me know its done');
}
console.log('ive been asked to start task', name);
setTimeout(() => {
console.log('500ms elapsed; starting task', name);
workToDo(done);
}, 500);
}
task('sample', (done) => {
console.log('doing work');
setTimeout(() => {
console.log('1 second has elapsed. calling back to let it know i am done');
done();
}, 1000)
});
The constructor for a promise has a similar purpose. You say "i'd like to create a promise, using the following code", and then it calls your code passing in two functions that it just created (usually named resolve and reject). These are used to tell the promise "i'm done successfully" or "i'm done with an error", and thus move the promise to a resolved state or a rejected state.
Related
I am capturing a value from an API call and have set it to a variable. I would now like to use that variable as a URL parameter in a second API call. This is probably super simple for a lot of folks but I'm just starting out learning javascript and everything I'm reading and trying is not working for me. I'd appreciate any help you can offer and I'm happy to add detail if you like!
This has been answered many times before (I gave at least two similar answers here and here).
You can basically do two things:
nest the commands:
it('test', function () {
cy.request().then( resp => {
return cy.visit(`/path/${response.body}`);
});
});
or, if you don't like callback hell, there are many patterns. Here's three:
(note, in following examples you don't gain anything as opposed to nesting as shown above because all these examples nest at minimum once. But these patterns may still be preferable in case you'd need to nest more than once, or if you need to reuse the variable much later in the test and don't want to put all commands into the first callback).
it('test', function () {
let value;
cy.request().then( resp => {
value = response.body;
});
cy.then(() => {
return cy.visit(`/path/${value}`);
});
});
or (using mocha context via Cypress' .as() abstraction):
it('test', function () {
let value;
cy.request().then( resp => {
cy.wrap(response.body).as('value');
});
cy.get('#value').then( value => {
return cy.visit(`/path/${value}`);
});
});
or (using mocha context directly):
it('test', function () {
cy.request().then( resp => {
// store as mocha context
// (note: in this pattern it's important the test case function is
// regular, non-arrow function; and the callback passed to `.then`
// is an arrow function so that you have access to parent
// lexical context via `this`)
this.value = response.body;
});
cy.then(() => {
return cy.visit(`/path/${this.value}`);
});
});
Following the documentation, I am using this in my JS:
var store = StoreClient('my secret');
store.set('active', true);
var status = store.get('active');
The variable status never has a value. I'm clearly not using the library correctly.
For context, this is inside a switch statement that does something like this for many of the cases, where some of them need to set or get a value from the StoreClient.
The documentation uses this example:
var store = StoreClient('your secret here');
store
.set('hello', 'world')
.then(function() {
return store.get('hello');
})
.then(function(value) {
// value === 'world'
return store.delete('hello');
})
.then(function() {
callback();
})
.catch(callback);
Because I'm on the amateur side, I'm not super familiar with promises. In that example, it's unclear to me which parts of the are required in order to [a] set, and eventually, [b] get a value. I suggest including an example that doesn't have set/get/delete combined into one.
I tried this:
var store = StoreClient('my secret');
store
.set('active', true)
.then(function() {
return store.get('active');
})
.then(function() {
callback();
})
.catch(callback);
... but then I get an error that there is no output variable, even though I haven't touched the output variable at the bottom of the script.
David from Zapier's Platform team here.
Sorry about the confusion in the docs. I'll give you a quick answer on how to fix your code and a long one as to why. In the meantime, I'll make a note to update the docs with more sensical examples.
Short
Two big things:
A. Promises pick whatever was returned in the last function. If you don't bring them along, they're lost. Your code should read:
.then(function(storedVal) { // <- that variable is missing in your code
console.log('stored val is', storedVal);
})
B. You need to provide a value to the second argument of callback. There's a better example here.
.then(function(storedVal) {
callback(null, {active: storedVal});
})
Long
Here's some of the nitty gritty on how to make all Zapier code work great.
Callback
Your code runs inside AWS Lambda, which always needs to know when you're finished. It executes all of your code in a special function with a certain set of arguments. The pertinent one here is callback, a function that you can call when you're ready to exit (or have an error). You can read more about that setup here.
Like most node callbacks, the callback has the function signature callback (error, result). To throw an error, you pass something in the first spot:
callback({msg: 'thing went wrong'});
To pass a result, use the second (and nothing in the first)
callback(null, {myData: 4});
So, not passing anything there is why the zap result isn't seeing any data.
Promises
In general, callbacks suck and are confusing to work with, so we designed StoreClient to return promises. There's a lot of materials about promises online so I won't go into the details here. The important thing is that whatever gets returned from a promise's function is the argument in the next one. For example:
Promise.resolve(1)
.then(function(val) {
// val === 1
return Promise.resolve(val + 1)
})
.then(function(val) {
// val === 2
})
There's a more practical example in these docs:
var store = StoreClient('your secret here');
var outCount;
store
.get('some counter')
.then(function(count) {
count = (count || 0) + 1;
outCount = count;
return store.set('some counter', count);
})
.then(function() {
callback(null, {'the count': outCount});
})
.catch(callback);
Hopefully that clears things up a bit!
Also, if you want to give Python a try, you can do the same code, but much simpler (example here).
Either way, let us know if there's anything else we can do to help!
I've hit a very weird problem: I'm trying to make unit tests to achieve 100% testing coverage on my application.
And of course I wrote some tests for my controllers but it seems like there is no way to test anything async in Ember (2.4.0) using ember-cli.
I have a function in controller that does this:
readObject() {
this.store.findRecord('myModel',1).then(function(obj) {
this.set('property1',obj.get('property2');
}.bind(this));
}
I'm writing a test that should cover this function.
test('action readObject', function (assert) {
const cont = this.subject();
cont.readObject();
assert.equal(cont.get('property1'), 'someValue);
});
Obivously, this assert wouldn't work because readObject() is async call but this isn't the root of the problem. The problem is that then a callback in this.store.findRecord is being executed - my controller is already destroyed! So I get "calling set on destroyed object" error there.
In other words - even if I wrap my function in a promise and reformat both functions like this:
readObject() {
return new Promise(function(resolve) {
this.store.findRecord('myModel',1).then(function(obj) {
this.set('property1',obj.get('property2');
resolve();
}.bind(this));
}.bind(this));
}
and
test('action readObject', function (assert) {
const cont = this.subject();
cont.readObject().then(function() {
assert.equal(cont.get('property1'), 'someValue);
});
});
It wouldn't work, because after executing readObject() my controllers become immediately destroyed, not waiting for any callbacks.
So, it could be any async call instead of store.findRecord - it could be Ember.run.later, for example.
Does anybody had the same issue? I've read a lot of articles I can't believe that Ember with such a big community doesn't provide a way to make async unit tests.
If anyone has any clues - please give me a hint, cause I'm kinda lost here. At the moment I have two thoughts:
I'm making controllers wrong, Ember doesn't suppose any async operations inside of it. But even if I move async calls to services - I hit the same problem with writing unit tests for them.
I have to decompose my functions to
readObject() {
this.store.findRecord('myModel',1).then(this.actualReadObject.bind(this));
}
actualReadObject(obj) {
this.set('property1',obj.get('property2');
}
to have at least callbacks body covered with tests, but this means I never get 100% testing coverage in my app.
Thank you in advance for any clues. :)
I had a similar problem and looking at the QUnit API - async I solved it. Try out following:
// ... in your controller ...
readObject() {
return this.store.findRecord('myModel',1).then(function(obj) {
this.set('property1', obj.get('property2');
}.bind(this));
}
// ... in your tests, either with assert.async: ...
const done = assert.async(); // asynchronous test due to promises usage
Ember.run(function(){
subject.readObject().then(function(){
// once you reach this point you are sure your promise has been resolved
done();
});
});
// or without assert.async
let promise;
Ember.run(function(){
promise = subject.readObject();
});
return promise;
In a case of unit tests I do also mock other dependencies, for example: store.
this.subject({
property1: null,
store: {
findRecord(modelName, id){
assert.equal(modelName, "myModel1");
assert.equal(id, 1);
return new Ember.RSVP.Promise(function(resolve){
resolve(Ember.Object.create({ property2: "a simple or complex mock" }));
})
}
}
});
I am not sure about the second case (the one without assert.async). I think it would work too, because the test suite returns a promise. This gets recorgnized by QUnit that waits for the promise.
I copy my own solution here, cause code formatting in comments isn't too good.
test('async', function (assert) {
const cont = this.subject();
const myModel = cont.get('store').createRecord('myModel'); //
// Make store.findRecord sync
cont.set('store',{
findRecord(){
return { then : function(callback) { callback(myModel); } }
}
});
// Sync tests
assert.equal(cont.get('property2'), undefined);
cont.readObject(); // This line calls store.findRecord underneath
assert.equal(cont.get('property2'), true);
});
So, I just turned store.findRecord into a sync function and it runs perfect. And again - many thanks to Pavol for a clue. :)
I have a situation where I am creating a node module that returns only when an asynchronous operation is completed. One way to do this (shown below), is to assign module.exports a function with a callback parameter. Inside the function, you would then return the callback.
Here's an example of what I describe, with done being callback:
// module called test.js
module.exports = function(done) {
// do something asynchronous here
process.nextTick(function() {
done(); // call done when the asynchronous thing is complete...
}
}
Where I am getting hung up, is in how the callback done is indeed being executed, considering I don't define it anywhere...
For example, in vanilla javascript, I can pass done as parameter and then call it within the function, as long as I create the callback function in the invocation.
function testAsyncCb(msg, done) {
console.log(msg);
setTimeout( function() {
done();
}, 1000);
console.log("last line in code");
}
testAsyncCb("testing", function(){ console.log("done"); }); // invocation with callback function
Back in the first node example, somewhere the call to module.exports by require() is creating a function for the done() to resolve to right? If not, how is the callback resolving?
Having a hard time finding information for how this works. Any help/direction is appreciated.
Think of module.exports as an object (module.exports = {}). So whatever you put to the object will be publicly visible to anyone do require module.
For instance, you have
module.exports = function myFunc() {}
then require to that would mean
var abc = require('./my-module'); --> abc == myFunc
if you would do
module.export.myFunc = function () {}
than require would be
var abc = require('./my-module'); --> abc == {myFunc: function () {}}
require operation is sync, not async like in requirejs (meaning not AMD but more like commonjs).
see http://www.sitepoint.com/understanding-module-exports-exports-node-js/
for more info
also for nodejs official docs: https://nodejs.org/api/modules.html
I'm asking this question so I can learn the 'best practice' way of doing something in javascript. Say I have this code here:
var someFunc = function () {
if (something) {
// do something
}
if (somethingElse) {
// do somethingElse
}
};
The question is what would be the best way to ensure that the 'something' is always ran BEFORE the 'somethingElse'. Since javascript is asynchronous, I understand that I would need some sort of callback system to ensure this. However, is there an easier way to refactor this? What if there are many if statements? What are the best libraries to do something like this cleanly? Thanks in advance.
Not all lines of code run asynchronously in Javascript. It depends on your code. For example:
var someFunc = function () {
if (something) {
console.log('something');
}
if (somethingElse) {
console.log('something else');
}
};
Will always write the following output:
something
something else
However if instead of printing the values you are calling a function that will be run later (like an Ajax request or a setTimeout callback), there is no guarantee that your code is run in the exact order. This behaviour depends on the function you are calling. For example the JQuery $.get() function is asynchronous (which means it will call your function at a later time that is not in your control) like this:
var someFunc = function () {
if (something) {
$.get('some-file.txt').done(function (result) {
console.log(result);
});
}
if (somethingElse) {
$.get('some-other-file.txt').done(function (result) {
console.log(result);
});
}
};
The resulting output can be the contents of 'some-file.txt' and 'some-other-file.txt' in any other.
As a rule of thumb whenever you are passing a function to another function (callbacks) you may be using the asynchronous feature of Javascript.
Nested callbacks
One way to solve this issue is to call the second asynchronous call in the first function:
var someFunc = function () {
if (something) {
$.get('some-file.txt').done(function (result1) {
console.log(result1);
if (somethingElse) {
$.get('some-other-file.txt').done(function (result2) {
console.log(result2);
});
}
});
}
};
But as you might have guessed this code will be hard to read.
Promises to the rescue
With Promises you can have a code that is easier to read.
Let's write the above ugly code with promises:
var someFunc = function () {
if (something) {
$.get('some-file.txt').then(function (result1) {
console.log(result1);
if (somethingElse) {
return $.get('some-other-file.txt');
}
}).then(function (result2) {
console.log(result2);
});
};
In general, promises make the code more readable and avoid too many nested callbacks. You can chain promises and it will read like synchronous code but it actually runs asynchronously.
See these questions to get more information:
How do I chain three asynchronous calls using jQuery promises?
Chain of Jquery Promises
What's the catch with promises?
They are not supported in old browsers (but you can add them with a 3rd party library like ES6 Promise Polyfill.
Before the promises were officially standardized every library had their own implementation which are slightly incompatible (jQuery, Angular, Ember)
They are a new concept to learn so the learning curve will be a little steep for newcomers.
Javascript is not asynchronous.
Provided both the if conditions are satisfied, what is inside the first if will get executed first and then the contents of the second if will be executed.