how can we combine synchronous function, callback function and promise all together - javascript

If I have three types of functions, which return, respectively,
A synchronous value:
function a(){
return 'a';
}
A callback:
function b(callback){
setTimeout(() => {
callback('b');
}, 10);
}
a Promise:
function c() {
return Promise.resolve('c')
}
How can I combine the result of all 3 in a single promise? Can I create a function which will return ['a', 'b', 'c']? I do not want to change these three functions.
How can we combine a synchronous function, a callback function and a Promise all together?

Promisify the callback, and send all three through Promise.all.
function a() {
return "a";
}
function b(callback) {
setTimeout(() => {
callback('b');
}, 10);
}
const bProm = () => new Promise(b);
function c() {
return Promise.resolve('c')
}
Promise.all([
a(),
bProm(),
c(),
])
.then((results) => {
console.log(results);
});

Given the functions:
function a(){
return 'a';
}
function b(callback){
setTimeout(() => {
callback('b');
}, 10);
}
function c() {
return Promise.resolve('c');
}
You can wrap them all in a promise using Promise.all(). The solution will depend on your requirements and limitations.
The function b(callback) must be wrapped, or be changed to accomplish this, because it has a setTimeout within it.
Wrapped version
function bWrapped (callback) {
return new Promise(
resolve =>
b(()=> {
resolve(callback('b'));
}
)
}
A function to return the expected result would be like:
const abc = () => {
const functionAPromise = Promise.resolve(a())
const callback = function(b){return b}
const functionBPromise = bWrapped(callback)
return Promise.all([functionAPromise,functionBPromise,c()])
}
Changed version
function b(callback){
return new Promise(resolve => setTimeout(()=> resolve(callback('b')), 10))
}
A function to return the expected result would be like:
const abc = () => {
const functionAPromise = Promise.resolve(a())
const callback = function(b){return b}
return Promise.all([functionAPromise,b(callback),c()])
}

Just check the result of each function and handle them.
function a() {
return "a";
}
function b(callback) {
setTimeout(() => callback("b"), 10);
}
function c() {
return Promise.resolve("c");
}
const functionList = [a, b, c];
function runFunctionList(list, callback) {
const resultList = [];
const called = {};
function next() {
if (resultList.length === list.length) {
for (let i = 0; i < resultList.length; i++) {
if (resultList[i] === undefined) {
return;
}
}
callback(resultList);
resultList.length = 0;
}
}
list.forEach((li, index) => {
const ret = li(value => {
resultList[index] = value;
next();
});
if (ret instanceof Promise) {
// it's promise
ret.then(value => {
resultList[index] = value;
next();
});
} else if (ret === undefined) {
// it's callback function
} else {
// it's synchronous function
resultList[index] = ret;
}
});
next();
}
runFunctionList(functionList, allValue => {
console.log("allValue here:", allValue);
});

Sequential execution:
function a() {
return 'a';
}
function b(callback) {
setTimeout(() => {
callback('b');
}, 10);
}
function c() {
return Promise.resolve('c');
}
function abc() {
return new Promise(resolve => {
const valueA = a();
b(valueB => {
c().then(valueC => {
resolve([valueA, valueB, valueC]);
});
});
});
}
abc().then(result => {
console.log(result);
});

Related

Function calls from data.map are not executed synchronously

I am trying to call same function from map. Depending upon number of entries function will be called.
Here is my code
getDetails = (inputData) => {
const data = {
accountName: "test",
};
let url = `/rest/jarvis/reports/v1/getData`;
let thisdata = this;
post(url, data)
.then((response) => {
if (
response.data &&
response.data.sb &&
response.data.sb.length > 0
) {
this.props.bs.betDetails = {
data: [...response.data.sportsbookBets],
exportAccesses: response.data.exportAccesses,
};
this.props.bs.betDetails.data.map((value, index) => {
console.log("Index Value --->", index);
this.getEventDetails(value, index);
}),
this.updateReducer({
...this.props.bs,
});
}
}
}
getEventDetails = async (value, index) => {
let _this = this;
let myPromise = new Promise(function (myResolve, myReject) {
console.log("Index Value at the entry--->", index);
const data = {
accountName: "test",
};
let url = `/rest/jarvis/reports/v1/getBetEventData`;
post(url, data).then((response) => {
if (response.data) {
console.log("Index Value inside--->", index);
_this.eventIdIndexMap.set(
list.eventId != null
? list.eventId
: _this.props.bs.betEventRequest.EventId,
index
);
_this.props.bs.testEventDetails[
_this.eventIdIndexMap.get(
list.eventId != null
? list.eventId
: _this.props.bs.betEventRequest.EventId
)
] = _this.props.bs.betEventResponse;
}}}
await myPromise;
}
Here problem I am facing is inside post call of 'getEventDetails' index values are not getting in sequence.
I have used promise await and async but it is not working.
Can someone please tell me how to get it properly ?
map calls its callback function without await. You can use asynchronous functions in the callback function but they won't be executed in order.
But you can create your own map with await:
async function map(array, cb) {
const result = [];
for (const element of array) {
result.push(await cb(element));
}
return result;
}
async function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
(async () => {
console.log(await map([3, 2, 1], async el => { await sleep(el * 100); return 2 * el; }));
})();
or create your own Array.prototype.syncMap:
Array.prototype.syncMap = async function(cb) {
const result = [];
for (const element of this) {
result.push(await cb(element));
}
return result;
}
async function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
(async () => {
console.log(await [3, 2, 1].syncMap(async el => { await sleep(el * 100); return 2 * el; }));
})();
It looks like you don't even need a result. You can write your own Array.prototype.syncForEach:
Array.prototype.syncForEach = async function(cb) {
for (const element of this) {
await cb(element);
}
}
async function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
[3, 2, 1].syncForEach(async el => { await sleep(el * 100); console.log(2 * el); });
[30, 20, 10].forEach(async el => { await sleep(el * 10); console.log(2 * el); });
As you can see in the output the functions in syncForEach are executed in order and the functions in forEach are executed out of order.

How to implement a fluent interface with Promises?

I want to make this code work
$('start') // push initial value to an array
.addItem(2) // add another value
.printAll() // print everything in array
.delay(2000) // wait x seconds
.addItem(5)
.printAll()
.start(); // execute all the commands before this line
I created a class with data array to hold the items. and steps array to hold the operations. Then i used chainable promises to execute them. is that the best way to do what i'm trying to achieve? Do I really need to store the operations in an array?
class Sync {
constructor() {}
init(startValue) {
this.data = [startValue];
this.steps = [];
return this;
}
addItem(value) {
const append = (v)=>this.data.push(v);
this._addStep(append, value);
return this;
}
printAll() {
this._addStep(v=>console.log(v.join()), this.data);
return this;
}
delay(value) {
this._addStep(window.setTimeout, value);
return this;
}
_addStep(fun, ...args) {
this.steps.push({
fun,
args
});
}
start() {
let start = Promise.resolve();
this.steps.forEach(({fun, args})=>{
start = start.then(()=>{
return new Promise(resolve=>{
if (fun === window.setTimeout) {
setTimeout(resolve, ...args);
} else {
fun.apply(this, args);
resolve();
}
}
);
}
);
}
);
return this;
}
}
const lib = new Sync();
const $ = lib.init.bind(lib);
$('start')
.addItem(2)
.printAll()
.delay(2000)
.addItem(5)
.printAll()
.start();
Although your question belongs to https://codereview.stackexchange.com/ according to me, I tried to think of another implementation without Promises. It works with closures and callbacks only:
class Sync {
constructor() {}
init(startValue) {
this.data = [startValue];
this.steps = [];
return this;
}
addItem(value) {
const append = v => this.data.push(v);
this._addStep(append, value);
return this;
}
printAll() {
this._addStep(v => console.log(v.join()), this.data);
return this;
}
delay(value) {
this._addStep(window.setTimeout, value);
return this;
}
_addStep(fun, ...args) {
this.steps.push({
fun,
args
});
}
start() {
let finalFunction;
this.steps.reverse().forEach(({ fun, args }) => {
if (fun === window.setTimeout) {
finalFunction = finalFunction ? encloseFunctionWithArgs(null, null, finalFunction, next => setTimeout(next, ...args)) : null;
} else {
finalFunction = encloseFunctionWithArgs(fun, args, finalFunction);
}
});
finalFunction();
return this;
}
}
function encloseFunctionWithArgs(fun, args, next, trigger) {
return function () {
if (fun)
fun(args);
if (next)
trigger ? trigger(next) : next();
}
}
const lib = new Sync();
const $ = lib.init.bind(lib);
$('start')
.addItem(2)
.printAll()
.delay(2000)
.addItem(5)
.printAll()
.start();
EDIT
I finally managed to achieve what you want:
const base = {
init(startValue) {
this.promise = new Promise(resolve => this.start = resolve).then(() => [startValue]);
return this;
},
addItem(value) {
this.promise = this.promise.then(v => [...v, value]);
return this;
},
printAll() {
this.promise.then(v => v.join()).then(console.log);
return this;
},
delay(value) {
this.promise = this.promise.then(v => new Promise(resolve => setTimeout(() => resolve(v), value)));
return this;
}
}
const factory = base => arg => Object.create(base).init(arg);
const $ = factory(base);
$('start')
.addItem(2)
.printAll()
.delay(2000)
.addItem(5)
.printAll()
.start();

Wait for a functions to finish in sequence Javascript

I have three functions prints 20,30,10 as per setTimeout how should i make them print 10,20,30 order using promise
How to write these callbacks to print right order.
P.S. : This is not a duplicate question. Thanks !
var A = function(callback) {
setTimeout(function() {
console.log(10)
callback();
}, 2000);
};
var B = function(callback) {
console.log(20);
callback();
};
var C = function(callback) {
setTimeout(function() {
console.log(30)
callback();
}, 200);
};
function runTask() {
var wait = ms => new Promise ((resolve,reject) => setTimeout(resolve, ms))
var FuncA = wait();
FuncA.then(() => A(function () {}))
. then(() => B(function () {}))
.then(() => C(function () {}));
}
runTask();
I'm not 100% sure I understood your question. But, here it is based on what I understood. You weren't doing anything with the callback so I didn't pass them.
In your code function B didn't have a delay.
function delayAsync(ms) {
return new Promise(p_resolve => setTimeout(p_resolve, ms));
}
async function A(callback) {
await delayAsync(2000);
console.log(10);
if (callback instanceof Function) {
callback();
}
}
async function B(callback) {
console.log(20);
if (callback instanceof Function) {
callback();
}
}
async function C(callback) {
await delayAsync(200);
console.log(30);
if (callback instanceof Function) {
callback();
}
}
function runTask() {
return new Promise(async (resolve, reject) => {
await A();
await B();
await C();
resolve();
});
}
runTask().then(() => console.log('done'));
If you want to stick with Promises, you could create a helper function that performs a setTimeout but returns a Promise. This will preserve the order of console logs:
const setTimeoutAsync = (fn, ms) => new Promise(resolve => setTimeout(() => resolve(fn()), ms));
var A = function(callback) {
return setTimeoutAsync(() => {
console.log(10)
callback();
}, 2000);
};
var B = function(callback) {
console.log(20)
callback();
};
var C = function(callback) {
return setTimeoutAsync(() => {
console.log(30)
callback();
}, 200);
};
function runTask() { // refactored since A now returns a Promise
return A(() => {})
.then(() => B(() => {}))
.then(() => C(() => {}));
}
runTask();
Alternatively, if you'd like a clean solution and don't mind adding a third party module, you could use async-af, a library for chainable asynchronous JavaScript methods that I maintain:
const setTimeoutAsync = (fn, ms) => new Promise(resolve => setTimeout(() => resolve(fn()), ms));
var A = function(callback) {
return setTimeoutAsync(() => {
console.log(10)
callback();
}, 2000);
};
var B = function(callback) {
console.log(20)
callback();
};
var C = function(callback) {
return setTimeoutAsync(() => {
console.log(30)
callback();
}, 200);
};
// to run in parallel you would omit `series`, but
// since you want to run the tasks in order include it:
function runTask(...tasks) {
return AsyncAF(tasks).series.forEach(task => task(() => {}));
}
runTask(A, B, C);
<script src="https://unpkg.com/async-af#7.0.11/index.js"></script>

How can I return a value for a function from inside a function inside the function I wish to return

I wish to be able to return and end a function from inside a function inside that function. Just typing return inside the function will return for that function instead of the one I'm trying to return for.
function iWannaEscapeThis() {
function someSideFunction() {
//need to return iWannaEscapeThis here somehow
}
}
You need to call it main function and return it
function iwannaescapethis() {
function somesidefunction() {
return 'from inside'
}
return somesidefunction();
}
console.log(iwannaescapethis())
Async function
async function iwannaescapethis() {
async function somesidefunction() {
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("from inner"), 2000)
});
return await promise;
}
let x = await somesidefunction()
return x;
}
(async function(){
let res = await iwannaescapethis()
console.log(res)
})()
Return the nested function:
function iwannaescapethis() {
function somesidefunction() {
return "sideFunction";
}
return somesidefunction();
}
console.log(iwannaescapethis());
It's possible to use exceptions for doing this, but a big warning it's not advised you use exceptions for flow control too often.
But if you really do need this feature, here is an example.
class ReturnException extends Error {
constructor (value) { super(); this.value = value; }
};
function thisIsTheOuterFunction() {
try {
function someSubFunction() {
function someOtherSubFunction() {
throw new ReturnException("The answer is 42");
}
someOtherSubFunction();
}
someSubFunction();
} catch (e) {
if (e instanceof ReturnException) {
return e.value;
} else throw e;
}
}
console.log(thisIsTheOuterFunction());

Calling an async function with default parameter as function for arguments checking handled synchonous?

I am using async functions and default parameters with evaluation at call time.
With the default parameters I use a function to check if a value is provided or not.
function mandatory(paramName) {
throw new Error(`Missing parameter: ${paramName}`)
}
async function foo({ a, b = mandatory('b') }) {
return Promise.resolve(b)
}
// uses chai.assert and chai-as-promised
describe('foo', () => {
it('should return a rejected promise', async () => {
const promise = foo({ a: 'hi' })
assert.isRejected(promise, /Error: Missing parameter: b/)
})
})
This test fails with an error:
Error: Missing parameter: b
because this exception is thrown outside the async flow as you can see here:
var foo = function () {
var _ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee(_ref2) {
var _ref2$a = _ref2.a,
a = _ref2$a === undefined ? 'a' : _ref2$a,
_ref2$b = _ref2.b,
b = _ref2$b === undefined ? mandatory('b') : _ref2$b;
return regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
return _context.abrupt('return', Promise.resolve(b));
case 1:
case 'end':
return _context.stop();
}
}
}, _callee, this);
}));
return function foo(_x) {
return _ref.apply(this, arguments);
};
}();
function _asyncToGenerator(fn) {
return function () {
var gen = fn.apply(this, arguments);
return new Promise(function (resolve, reject) {
function step(key, arg) {
try {
var info = gen[key](arg);
var value = info.value;
} catch (error) {
reject(error); return;
}
if (info.done) {
resolve(value);
} else {
return Promise.resolve(value).then(function (value) {
step("next", value);
},
function (err) {
step("throw", err);
});
}
}
return step("next");
});
};
}
My question: is this per spec or per implementation? I would expect the promise to be rejected, not thrown.
This is probably a bug in Babel, because in the latest Chrome (which supports async functions with a flag) and in Firefox 52, this code works as expected (i.e. the promise is rejected):
function mandatory(paramName) {
throw new Error(`Missing parameter: ${paramName}`)
}
async function foo({ a, b = mandatory('b') }) {
return Promise.resolve(b)
}
foo({a: 'hi'}).catch(error => console.log('rejected')) // logs 'rejected'

Categories