The below script works, but I don't understand why one doesn't have to
const a = promisify(opA());
instead of the (correct)
const a = promisify(opA);
I mean opA is a function, so why not opA()?
'use strict'
const { promisify } = require('util')
const opA = (cb) => {
setTimeout(() => {
cb(null, 'A')
}, 500)
}
const opB = (cb) => {
setTimeout(() => {
cb(null, 'B')
}, 250)
}
const opC = (cb) => {
setTimeout(() => {
cb(null, 'C')
}, 125)
}
const a = promisify(opA);
const b = promisify(opB);
const c = promisify(opC);
(async function() {
try {
console.log(await a());
console.log(await b());
console.log(await c());
} catch(err) {
print(err);
};
})();
I mean opA is a function, so why not opA()?
Because opA is a reference to the function itself. The promise will use that reference to execute that function at a later time.
Alternatively, opA() executes the function (without any arguments) now and passes the result of that function call to the promise. Since your opA function doesn't return anything, it would pass undefined to the promise. The promise would then have nothing to execute later after it completes its operation(s). The setTimeout would then also fail because cb is also undefined, because no arguments were passed to opA().
Any time you do see a structure like that, either (1) it's a bug or (2) the function intentionally builds and returns a callback function intended for the promise. For example, if you write a function which returns opA then you can invoke that function and pass its result to promisify.
An important clue to this behavior is here:
const opA = ...
opA is a variable, not unlike any other variable. It contains a value or a reference to something. In this case it's a reference to a function. You could re-assign that variable, pass it as a function argument, set it as a property on an object, etc. just like any other.
Related
I am currently stuck on a problem with the memoize function in JavaScript. The last 2 tests don't pass. I will be more then happy for assistance.
The problem is the following:
Write a memoize function that takes in a required callback function and an optional resolver function. The memoize function returns a memoized version of the callback function, which is defined as follows:
All of the return values of the memoized function are cached. If the memoized callback is called with an existing cache key (defined below), then that cached value is returned without invoking the callback again.
The cache key is defined based on the optional resolver function. If a resolver function is not provided, then the cache key is the result of passing the memoized function arguments to JSON.stringify as an array. If a custom resolver function is provided, then the arguments should be individually passed to that function instead, and its return value will be the cache key (note that this can be of any type).
The memoized function should also have three methods: clear(): Clears out the cache. delete(...args): Deletes the cache entry corresponding to the passed arguments if one exists. has(...args): Returns a boolean of true if the cache has an entry corresponding to the passed arguments, otherwise false. For simplicity, you don't need to worry about binding a this context (i.e., you can assume that the callback doesn't reference this).
The callback function look like this:
const callback = (...args) => args;
And this is the input and the result that should be returned
const memoized = memoize(callback);
memoized(123); // calls callback, returns 123
memoized(123); // returns 123
memoized(123, 'abc'); // calls callback, returns [123, 'abc']
const memoized2 = memoize(callback, args => args[0]);
memoized2(123); // calls callback, returns 123
memoized2(123); // returns 123
memoized2(123, 'abc'); // returns 123
memoized('abc', 123); // calls callback, returns ['abc', 123]
memoized2('abc'); // returns ['abc', 123]
I've stored all arguments in cache and when the same argument is called the result will be returned from cache.
This is my code
function memoize(cb) {
const memo = new Map();
return function (...args) {
const key = JSON.stringify(args);
if (!memo.has(args)) {
memo.set(key, cb(...args));
return memo.get(key);
}
return memo.get(key);
};
}
And this are the tests
const chai = require("chai");
const spies = require("chai-spies");
const { expect } = chai;
chai.use(spies);
const spy = () => chai.spy(() => {});
const { memoize } = require("../memoize.js");
describe("memoize", () => {
it("callback without parameters is never called twice", () => {
const callback = spy(() => {});
const memoized = memoize(callback);
expect(callback).to.have.been.called.exactly(0);
memoized();
expect(callback).to.have.been.called.exactly(1);
memoized();
expect(callback).to.have.been.called.exactly(1);
memoized();
memoized();
expect(callback).to.have.been.called.exactly(1);
});
it("callback with a single parameter is handled properly", () => {
const callback = spy((val) => val * 2);
const memoized = memoize(callback);
expect(callback).to.have.been.called.exactly(0);
const val1 = memoized(1);
expect(callback).to.have.been.called.exactly(1);
expect(val1).to.equal(2);
const val2 = memoized(1);
expect(callback).to.have.been.called.exactly(1);
expect(val2).to.equal(2);
const val3 = memoized(2);
expect(callback).to.have.been.called.exactly(2);
expect(val3).to.equal(4);
const val4 = memoized(2);
expect(callback).to.have.been.called.exactly(2);
expect(val4).to.equal(4);
const val5 = memoized(1);
expect(callback).to.have.been.called.exactly(2);
expect(val5).to.equal(2);
});
it("has function works as expected", () => {
const callback = spy((...args) => args);
const memoized = memoize(callback);
expect(memoized.has()).to.be.false;
expect(memoized.has(123)).to.be.false;
expect(memoized.has(123, "abc")).to.be.false;
memoized();
expect(memoized.has()).to.be.true;
memoized(123);
expect(memoized.has(123)).to.be.true;
memoized(123, "abc");
expect(memoized.has(123, "abc")).to.be.true;
expect(callback).to.have.been.called.exactly(3);
});
});
The solution I am gonna give solves 12 tests instead of 3, including your tests.
Try this:
const defaultResolver = (...args) => JSON.stringify(args);
export function memoize(fn, resolver = defaultResolver) {
const cache = new Map();
const memoized = (...args) => {
const key = resolver(...args);
if (cache.has(key)) {
return cache.get(key);
}
const result = fn(...args);
cache.set(key, result);
return result;
};
memoized.clear = () => cache.clear();
memoized.delete = (...args) => {
const key = resolver(...args);
cache.delete(key);
};
memoized.has = (...args) => {
const key = resolver(...args);
return cache.has(key);
}
return memoized;
}
I'm trying to return a debounced search result from an API request using lodash's debounce function but keep getting undefined from the call.
Here's my code, please help;
const searchSuggestionsRequest = async (input) => {
const params = {
userInput: encodeURIComponent(input),
};
const { data } = await axios.get(`${BASE_URL}/api/location`, { params });
return data;
};
const debouncedSuggestionsRequest = _.debounce(searchSuggestionsRequest, 500);
const fetchSearchSuggestions = (input) => {
return debouncedSuggestionsRequest(input);
};
handleSearchSuggestions = async (input) => {
const searchObj = await fetchSearchSuggestions(input);
console.log('searchObj', searchObj);
};
handleSearchSuggestions()
You are expecting the debounce function to return the result of your original function, or in your case the resolved promise. But that is not how the debounce function works.
The debounce function wraps your function with its own code in which it checks if any new call files in our not. After a certain amount of time eventually your function is initiated. But it cannot return the result of that function.
You need to define a more global scope (or at least a scope overlapping your functions) variable, and set that variable in your function where you get the axios result.
You problem remains that you cannot await for the result, so your console.log will still be undefined. Personally I develop in Vue, and I can set a reactivity watcher on the variable.
I have an async function f that calls another async function g. To test if f calls g, I'm stubbing g using sinon and assert it's been called using should.js.
'use strict';
require('should-sinon');
const sinon = require('sinon');
class X {
async f(n) {
await this.g(n);
// this.g(n); // I forget to insert `await`!
}
async g(n) {
// Do something asynchronously
}
}
describe('f', () => {
it('should call g', async () => {
const x = new X();
sinon.stub(x, 'g').resolves();
await x.f(10);
x.g.should.be.calledWith(10);
});
});
But this test passes even when I forget to use await when calling g in f.
One of the ways to catch this error is to make the stub return a dummy promise and check if its then is called.
it('should call g', async () => {
const x = new X();
const dummyPromise = {
then: sinon.stub().yields()
};
sinon.stub(x, 'g').returns(dummyPromise);
await x.f(10);
x.g.should.be.calledWith(10);
dummyPromise.then.should.be.called();
});
But this is a bit bothersome. Are there any convenient ways to do this?
Your example for f shows flawed code design which becomes more obvious if you write the same function without async/await syntax:
f(n) {
return g(n).then(()=>{});
}
This achieves the same behavior - whether g resolved becomes hard to tell (assuming you don't know if f returned g's promise, which is the same as not knowing whether f awaited g). If f is not interested in the result of g it should just simply return it, not hide it. Then you can simply test for the result.
If your point is that f might have to trigger several async calls sequentially awaiting several g_1, g_2,... to resolve, then you can build a test chain by asserting in the stub of g_n+1 that the dummy-promise of g_n has been resolved. In general your approach to test a dummy-promise for its status is fine.
Instead of stubbing then, you're best off stubbing g in such a way that it sets some boolean on the next event loop iteration. Then, you can check this boolean after calling f to make sure f waited for it:
it('should call g', async () => {
const x = new X();
let gFinished = false;
sinon.stub(x, 'g').callsFake(() => {
return new Promise((resolve) => {
setImmediate(() => {
gFinished = true;
resolve();
});
});
});
await x.f(10);
x.g.should.be.calledWith(10);
gFinished.should.be.true();
});
Edit: Of course, this isn't a perfect guarantee because you could have f wait on any promise that waits at least as long as it takes for g to resolve. Like so:
async f(n) {
this.g(n);
await new Promise((resolve) => {
setImmediate(() => {
resolve();
});
});
}
This would cause the test I wrote to pass, even though it's still incorrect. So really it comes down to how strict you're trying to be with your tests. Do you want it to be literally impossible to have a false positive? Or is it ok if some obvious trickery can potentially throw it off?
In most cases I find that the latter is ok, but really that's up to you and/or your team.
Perhaps i'm not googleing correctly. does a then function without a paramater not block? for instance, you have a promise:
someFunc = () => {
return new Promise((res,rej)=>{
somethingAsync(input).then((val) => res(val))
})
}
in the following implements of our function. would both wait for the someFunc return val?
someFunc().then(dosomethingafter())
someFunc().then((val) => dosomethingafter())
In JS expressions are eagerly evaluated. It means every function argument is evaluated before it's passed.
someFunc().then(dosomethingafter())
is effectively identical to
var tmp = dosomethingafter();
someFunc().then(tmp)
so a function someFunc().then(dosomethingafter()) is invoked before then is called, and its returned result is passed as a parameter.
What you probably meant instead is
someFunc().then(dosomethingafter)
note there is no function call - only a reference to a function is passed to then and it would then be called when a promise is resolved.
It is easier to illustrate this via examples. Your first case:
const fn = (text) => {console.log(text)}
const waiter = () => new Promise((resolve, reject) => {
return setTimeout(() => {
fn('resolved')
resolve()
}, 2000)
})
waiter().then(fn('done'))
Notice that fn got executed first, got evaluated and then the waiter got executed.
Lets look at the 2nd case:
const fn = (text) => {console.log(text)}
const waiter = () => new Promise((resolve, reject) => {
return setTimeout(() => {
fn('resolved')
resolve()
}, 2000)
})
waiter().then(() => fn('done'))
Notice now that we got resolved first and then done.
So the answer to your question is yes in both cases we will wait and execute the someFunc or in the above example the waiter.
The main difference really is when does your dosomethingafter get executed.
In the first case it is right away and then it is passed in the waiter.
In the second case you have a valid promise chain which will first be executed and then once done (and since fn acts as the function handler of the then) it will execute dosomethingafter.
pass doSomethingafter first class
const handleAsJson = response => response.json()
fetch(url)
.then(handleAsJson)
.then(console.log)
You have a prototype object Foo with two async method calls, bar and baz.
var bob = new Foo()
Foo.prototype.bar = function land(callback) {
setTimeout(function() {
callback()
console.log('bar');
}, 3000);
};
Foo.prototype.baz = function land(callback) {
setTimeout(function() {
callback()
console.log('baz');
}, 3000);
};
We want to do bob.bar().baz() and have it log "bar" and "baz" sequentially.
If you cannot modify the method calls (including passing in your callback function), how can you pass a default callback into these method calls?
Some ideas:
Wrap "bob" with decorator (still fuzzy on how to implement, could use a small example)
Modify constructor to assign default callback if none assigned (have not considered if this is possible or not)
Use a generator wrapper that will continue to call next method until none are left?
The more recommended way instead is to use promises as this is the community-wide practice to do async work.
We want to do bob.bar().baz() and have it log "bar" and "baz"
sequentially.
Why would you want to do that just to achieve this bob.bar().baz() "syntax"? You could do it pretty neatly using the Promise API w/o additional efforts to make that syntax work that indeed increases code complexity reducing the actual readability.
So, you might want to consider using the promise-based approach like this. It offers much flexibility than what you would have achieved with your approach:
Foo.prototype.bar = function () {
return new Promise(function (resolve) {
setTimeout(function () {
resolve()
console.log('bar');
}, 3000);
};
};
Foo.prototype.baz = function () {
return new Promise(function (resolve) {
setTimeout(function () {
resolve()
console.log('baz');
}, 3000);
};
};
Now you'd do this to run them sequentially one after another:
var bob = new Foo();
bob.bar().then(function() {
return bob.baz();
});
// If you're using ES2015+ you could even do:
bob.bar().then(() => bob.baz());
If you need to chain more functions you could simply do it:
bob.bar()
.then(() => bob.baz())
.then(() => bob.anotherBaz())
.then(() => bob.somethingElse());
Anyway, if you're not used to using promises you might want to read this
Warning this isn't quite right yet. Ideally we'd subclass Promise and have proper then/catch functionality but there are some caveats with subclassing bluebird Promise. The idea is to store an internal array of promise generating functions, then when a Promise is waited on (then/await) serially wait on those promises.
const Promise = require('bluebird');
class Foo {
constructor() {
this.queue = [];
}
// promise generating function simply returns called pGen
pFunc(i,pGen) {
return pGen();
}
bar() {
const _bar = () => {
return new Promise( (resolve,reject) => {
setTimeout( () => {
console.log('bar',Date.now());
resolve();
},Math.random()*1000);
})
}
this.queue.push(_bar);
return this;
}
baz() {
const _baz = () => {
return new Promise( (resolve,reject) => {
setTimeout( () => {
console.log('baz',Date.now());
resolve();
},Math.random()*1000);
})
}
this.queue.push(_baz);
return this;
}
then(func) {
return Promise.reduce(this.queue, this.pFunc, 0).then(func);
}
}
const foo = new Foo();
foo.bar().baz().then( () => {
console.log('done')
})
result:
messel#messels-MBP:~/Desktop/Dropbox/code/js/async-chain$ node index.js
bar 1492082650917
baz 1492082651511
done
If you want to avoid callback hell and keep your sanity ES6 promises are the most appropriate approach for the sake of functional programming. You just chain up your sequential asynchronous tasks in the asynchronous timeline just like working in a synchronous timeline.
In this particular case you just need to promisify your asynchronous functions. Assume that your asynch functions takes a data and a callback like asynch(data,myCallback). Let us assume that the callback is error first type.
Such as;
var myCallback = (error,result) => error ? doErrorAction(error)
: doNormalAction(result)
When your asynch function is promisified, you will actually be returned a function which takes your data and returns a promise. You are expected to apply myCallback at the then stage. The return value of myCallback will then be passed to the next stage at where you can invoke another asynch function supplied with the return value of myCallback and this goes on and on as long as you need. So let's see how we shall implement this abstract to your workflow.
function Foo(){}
function promisify(fun){
return (data) => new Promise((resolve,reject) => fun(data, (err,res) => err ? reject(err) : resolve(res)));
}
function myCallback(val) {
console.log("hey..! i've got this:",val);
return val;
}
var bob = new Foo();
Foo.prototype.bar = function land(value, callback) {
setTimeout(function() {
callback(false,value*2); // no error returned but value doubled and supplied to callback
console.log('bar');
}, 1000);
};
Foo.prototype.baz = function land(value, callback) {
setTimeout(function() {
callback(false,value*2); // no error returned but value doubled and supplied to callback
console.log('baz');
}, 1000);
};
Foo.prototype.bar = promisify(Foo.prototype.bar);
Foo.prototype.baz = promisify(Foo.prototype.baz);
bob.bar(1)
.then(myCallback)
.then(bob.baz)
.then(myCallback)