When I run the code, I get the error
mergePromise(...).then is not a function.
I want to know why I got this error.
const timeout = ms => new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, ms);
});
const ajax1 = () => timeout(2000).then(() => {
console.log('1');
return 1;
});
const ajax2 = () => timeout(1000).then(() => {
console.log('2');
return 2;
});
const ajax3 = () => timeout(2000).then(() => {
console.log('3');
return 3;
});
const mergePromise = ajaxArray => {
const data=[];
ajaxArray[0]().then(i=>data.push(i));
timeout(1005).then(() => {
ajaxArray[1]().then(i=>data.push(i));
});
timeout(10).then(() => {
ajaxArray[2]().then(i=>data.push(i));
});
return data;
};
I guess maybe the timeout function has some mistake. What am I doing wrong?
Maybe you can try return Promise.resolve(data) instead, it would return a promise, which is what you need.
Related
Trying to learn proper async/await JavaScript to run functions in sequence when an early function in the sequence would be delayed (using setTimeout to simulate). I'm not getting the expected results (still getting "first", "second", "this should run first?" - see code).
What am I missing? Do I have the wrong idea about this?
Thanks in advance!
const zeroFunction = () => {
setTimeout(() => {
return new Promise((resolve) => {
console.log("This should run first?");
resolve();
});
}, 2000)}
const firstFunction = () => {
return new Promise((resolve) => {
console.log("first");
resolve();
})
}
const secondFunction = () => {
return new Promise((resolve) => {
console.log("second");
resolve();
})
}
async function fnAsync() {
await zeroFunction();
await firstFunction();
secondFunction();
}
fnAsync();
zeroFunction is currently returning undefined implicitly, not a Promise. Inverse the wrapping of the setTimeout and Promise constructor and it should work as expected.
const zeroFunction = () => {
return new Promise((resolve) => {
setTimeout(() => {
console.log("This should run first?")
resolve()
}, 2000)
})
}
const firstFunction = () => {
return new Promise((resolve) => {
console.log("first")
resolve()
})
}
const secondFunction = () => {
return new Promise((resolve) => {
console.log("second")
resolve()
})
}
async function fnAsync() {
await zeroFunction()
await firstFunction()
secondFunction()
}
fnAsync()
On incoming webrtc call, I open a modal to show user a message about media permissions and await this till the user presses OK button on the modal.
I do it like this:
async function myMessage(){
$('#id_my_message_modal').modal('show');
return new Promise(resolve =>
$('#id_my_message_button').on('click', () => {
$('#id_my_message_modal').modal('hide');
resolve();
}
)
);
}
then
await myMessage();
The issue I am facing now is if await myMessage(); is called again while the previous call has still not returned(i.e user hasn't pressed OK button). I want a way to cancel any previous await myMessage();, if exists, before it is called again.
Is there any way to do it?
The first approach (Live demo)- add every call of the async function to the queue, so you will get a sequence of dialogs with the result returning (close/accept/whatever).
// decorator from my other answer
function asyncBottleneck(fn, concurrency = 1) {
const queue = [];
let pending = 0;
return async (...args) => {
if (pending === concurrency) {
await new Promise((resolve) => queue.push(resolve));
}
pending++;
return fn(...args).then((value) => {
pending--;
queue.length && queue.shift()();
return value;
});
};
}
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const myMessage = asyncBottleneck(async function () {
const $modal = $("#id_my_message_modal");
$modal.modal("show");
const result = await new Promise((resolve) =>
$modal.on("click", "button", (e) => {
$modal.modal("hide");
$modal.off("click", "button");
resolve($(e.target).data("action"));
})
);
await delay(250);
return result;
});
The second approach (Live demo)- multiplexing the fn calls, when every await of the function will return the same result; Compare console output of the live demos.
function singleThread(fn) {
let promise = null;
return function (...args) {
return (
promise ||
(promise = fn(...args).finally(() => {
promise = null;
}))
);
};
}
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const myMessage = singleThread(async function () {
const $modal = $("#id_my_message_modal");
$modal.modal("show");
const result = await new Promise((resolve) =>
$modal.on("click", "button", (e) => {
$modal.modal("hide");
$modal.off("click", "button");
resolve($(e.target).data("action"));
})
);
await delay(250);
return result;
});
$("#btn-run").on("click", async () => {
myMessage().then((c) => console.log(`First result: ${c}`));
await myMessage().then((c) => console.log(`Second result: ${c}`));
myMessage().then((c) => console.log(`Third result: ${c}`));
});
The third way- closing the previous modal with its promise rejecting (Live Demo open console there to see the result).
import CPromise from "c-promise2";
const showModal = (() => {
let prev;
return (id, text = "") => {
prev && prev.cancel();
return (prev = new CPromise((resolve, reject, { onCancel }) => {
const $modal = $(id);
text && $modal.find(".modal-body").text(text);
$modal.modal("show");
const dispose = () => {
$modal.modal("hide");
$modal.off("click", "button");
};
$modal.on("click", "button", function (e) {
dispose();
resolve($(this).data("action"));
});
$modal.on("hidden.bs.modal", () => {
setTimeout(() => resolve("close"));
});
onCancel(dispose);
})).finally(() => (prev = null));
};
})();
$("#btn-run").on("click", async () => {
showModal("#id_my_message_modal", "First message").then(
(c) => console.log(`First modal result: ${c}`),
(e) => console.warn(`First modal fail: ${e}`)
);
showModal("#id_my_message_modal", "Second message").then(
(c) => console.log(`Second modal result: ${c}`),
(e) => console.warn(`Second modal fail: ${e}`)
);
const promise = showModal("#id_my_message_modal", "Third message")
.then(
(c) => console.log(`Third modal result: ${c}`),
(e) => console.warn(`Third modal fail: ${e}`)
)
.timeout(5000)
.then(() => {
return showModal(
"#id_my_message_modal2",
"Blue Pill or Red Pill?"
).then((v) => console.log(`Pill: ${v}`));
});
/*setTimeout(()=>{
promise.cancel(); you can cancel the modal from your code
}, 1000);*/
});
I'm trying to Create a Promise that resolves when variable is not undefined.
Code example
https://codesandbox.io/s/quizzical-feynman-ktvox?file=/src/index.js
let fetchedPromise = new Promise((resolve, reject) => {
const check = ()=>{
setTimeout(() =>{
console.log("checking")
if (dataFetched) resolve(dataFetched);
else check()
}, 100);
}
check()
});
const waitForDataFromAnotherComponent = async () => {
let result = await fetchedPromise;
console.log("Result: ", result);
};
const assignData = () => {
setTimeout(() => {
dataFetched = 1000;
console.log(dataFetched);
}, 5000)
};
waitForDataFromAnotherComponent();
assignData();
This works but I find it inefficient as it's callstack prone and seems wrong.
Other non-working solutions I've tried:
//-------------SOLUTION 1
let fetchedPromise = new Promise((resolve, reject) => {
const check = ()=>{
if (dataFetched) resolve(dataFetched);
else check()
}
check()
});
//--------------------SOLUTION 2
let fetchedPromise = new Promise((resolve, reject) => {
if (dataFetched) resolve(dataFetched);
});
Scenario
I need a function like solution 3 that doesn't rely on setTimeout
Solved by using Javascript Proxy
Basically I assign a Proxy Object to dataFetched that listens to changes.
I re-create the function of listening due to the fact that it must include resolve()
let dataFetched
let x = {
aListener: function (val) {},
set a(val) {
dataFetched = val;
this.aListener(val);
},
get a() {
return dataFetched;
},
registerListener: function (listener) {
this.aListener = listener;
}
};
let fetchedPromise = new Promise((resolve, reject) => {
x.registerListener(function (val) {
console.log("yeyyyyyyyyyyyyyyy");
if (dataFetched) resolve(dataFetched);
});
});
const waitForDataFromAnotherComponent = async () => {
let result = await fetchedPromise;
console.log("Result: ", result);
};
const assignData = async () => {
await new Promise((resolve, reject) =>
setTimeout(() => {
x.a = 1000;
console.log(dataFetched);
resolve(dataFetched);
}, 1000)
);
};
waitForDataFromAnotherComponent();
assignData();
EDIT
Actually it's possible to externalize the resolve() function of the promise but with some downsides as stated here
example
let dataFetched
var promiseResolve, promiseReject;
let x = {
aListener: function (val) {
if (dataFetched) promiseResolve(dataFetched);
},
set a(val) {
dataFetched = val;
this.aListener(val);
},
get a() {
return dataFetched;
},
registerListener: function (listener) {
this.aListener = listener;
}
};
let fetchedPromise = new Promise((resolve, reject) => {
promiseResolve = resolve;
promiseReject = reject;
});
I'm trying to execute the functions one by one in synchronization.
var subtasks = ['Site', 'Draw', 'Material', 'Conduction', 'Cable', 'Install', 'Foundation']
function clickMe() {
return new Promise(resolve => {
jQuery("#addtoptasksubmit").trigger("click"); // triggering the button click
resolve("done click");
});
}
function typeWord(word) {
return new Promise(resolve => {
jQuery("#todotask").val(word); // input
resolve("done type");
});
}
function createSubTask() {
return new Promise(res => {
jQuery('#todotask').focus();
res("done")
})
};
function startLoop(i) {
new Promise(resolve => {
var promise = createSubTask();
promise.then(resolve => {
var typePromise = typeWord(subtasks[i]);
typePromise.then((resolve) => {
var clickPromise = clickMe();
clickPromise.then((resolve) => {
console.log(resolve);
});
});
});
})
}
let i = 0;
let prom = startLoop(i);
prom.then((res) => {
startLoop(i++);
})
code is not working properly and also I wanted to increment i automatically. For loop is a no show.
I've tried for loop and recursive async function on chrome.
Doesn't your startLoop(i) function need a return statement for the new Promise() call? I have to imagine this:
// prom = undefined as startLoop never returns anything
let prom = startLoop(i);
Change your code like so:
// Current code from description
function startLoop(i) {
new Promise(resolve => {
// Fixed code
function startLoop(i) {
return new Promise(resolve => {
var subtasks = ['Site', 'Draw', 'Material', 'Conduction', 'Cable', 'Install', 'Foundation'];
function clickMe() {
return new Promise(resolve => {
setTimeout(() => {
jQuery("#addtoptasksubmit").trigger("click");
resolve("done click");
}, 1000);
});
}
function typeWord(word) {
return new Promise(resolve => {
jQuery("#todotask").val(word);
resolve("done type");
});
}
function createSubTask() {
return new Promise(res => {
jQuery('#todotask').focus();
res("done");
})
};
function startLoop(i) {
return new Promise(res => {
var promise = createSubTask();
promise.then(
(resolve) => {
var typePromise = typeWord(subtasks[i]);
typePromise.then((resolve) => {
console.trace(resolve);
var clickPromise = clickMe();
clickPromise.then((resolve) => {
console.trace(resolve);
res("done loop " + subtasks[i]);
});
});
}
);
})
}
var _index_ = 0;
var _how_ = setInterval(() => {
if (_index_ < subtasks.length) {
let loopPromise = startLoop(_index_);
loopPromise.then((resolve) => {
_index_ += 1;
});
} else {
console.log("all done go get the new task");
clearInterval(_how_);
}
}, 10000);
I can optimise it further....
I need the function "finalizeProcess" to be executed once all the promises of the "processPhotos" function are finished.
Can anybody help me?
Thank!!
processPhotos();
finalizeProcess();
processPhotos (){
this.foto1File.generateBlob((blob) => {
ref.child('picture_1').put(blob)
.then((pictureRef) => {
//sentence
})
})
this.foto2File.generateBlob((blob) => {
ref.child('picture_2').put(blob)
.then((pictureRef) => {
//sentence
})
})
}
This will do the trick:
function processPhotos() {
var promise1 = new Promise(resolve => {
this.foto1File.generateBlob(blob => {
ref.child('picture_1').put(blob)
.then(pictureRef => {
resolve(pictureRef);
});
});
});
var promise2 = new Promise(resolve => {
this.foto2File.generateBlob(blob => {
ref.child('picture_2').put(blob)
.then(pictureRef => {
resolve(pictureRef);
});
});
});
Promise.all([promise1, promise2]).then(results => {
// do something with results here
finalizeProcess();
});
};
As to what trincot had said, you can use Promise.all
Store your functions into a variable and then resolve the promises together.
processPhoto(){
const firstPromise = this.fotoFile.generateBlob(blob => {
//[..]
}
const secondPromise = this.foto2File.generateBlob(blob => {
//[...]
}
const [foto1File, foto2File] = Promise.all([firstPromise, secondPromise]);
}
Of course in each function, make sure to be returning the promise itself.
Here another alternative, similar to the answer of davmich. But instead of calling finalizeProcess inside processPhotos a promise is returned, which you can use to call finalizeProcess.
function processPhotos() {
return Promise.all([
new Promise(resolve => {
this.foto1File.generateBlob(blob => {
ref.child('picture_1').put(blob).then(resolve);
});
}),
new Promise(resolve => {
this.foto2File.generateBlob(blob => {
ref.child('picture_2').put(blob).then(resolve);
});
})
]);
}
processPhotos().then(pictureRefs => finalizeProcess());
Alternatively you can unfold to do some processing first.
ref.child('picture_1').put(blob).then(resolve);
Becomes:
ref.child('picture_1').put(blob).then(pictureRef => {
// do stuff with pictureRef
resolve(/* custom value */);
});