Consider the following code:
export function* listen(channel, event, action) {
yield call(
[LaravelEchoManager, LaravelEchoManager.registerListener],
channel,
event,
(data) => {
// yield data;
},
);
}
The LaravelEchoManager.registerListener method registers an event listener for the given channel and the given event and calls the callback (data) => { ... } for each message received.
How do I yield the received data?
Well, you can but you have to yield something else. Like for example a promise:
export function* listen(channel, event, action) {
yield call(() => new Promise(done => {
LaravelEchoManager.registerListener(
channel,
event,
(data) => {
done(data);
}
);
}));
}
You can cheat using this kind of trick:
let received = [];
let active; // set to false to stop listening
export function* listen(channel, event, action) {
call(
[LaravelEchoManager, LaravelEchoManager.registerListener],
channel,
event,
(data) => {received[received.length] = data},
);
active = true; // start linstening
while(active){
while(received.length <= 0); // active wait
yield received.shift();
}
}
It answer the question, but I don't really advise to do this kind of thing.
Maybe you have to find another way to do your task.
Related
when i am trying to get this console.log(this.empresas[0]); it says it is undefined, but empresas is loaded in the function.
empresas: any;
constructor(...) {
this.getEmpresas();
console.log(this.empresas[0]);
}
getEmpresas(){
this.empresas = [];
this.http.get("http://url").subscribe( data => {
this.empresas = JSON.parse(data["_body"]);
}, err =>{
console.log(err);
});
}
Because just by calling getEmprass(), the value of this.emprass will not get updated. getEmprass() will immediately return without updating it. Later on, when the http request is completed, this.emprass gets updated, but that is after the time you printed its value.
Many things happen in Javascript asynchronously. Observables and subscribe function is one of them: its content gets executed asynchronously in the future (similar to callbacks).
You may wanna change your code to this:
constructor(...) {
this.getEmpresas();
console.log('hey, getEmpresas() finished!');
}
getEmpresas(){
this.empresas = [];
this.http.get("http://url").subscribe( data => {
this.empresas = JSON.parse(data["_body"]);
console.log(this.empresas[0]);//---------------------------> Print it here
}, err =>{
console.log(err);
});
}
If you want to wait in constructor function until the http request is finished, you should use an async function .
What I am trying to do is create a chrome extension that creates new, nested, bookmark folders, using promises.
The function to do this is chrome.bookmarks.create(). However I cannot just
loop this function, because chrome.bookmarks.create is asynchronous. I need to wait until the folder is created, and get its new ID, before going on to its children.
Promises seem to be the way to go. Unfortunately I cannot find a minimal working example using an asynchronous call with its own callback like chrome.bookmarks.create.
I have read some tutorials 1, 2, 3, 4. I have searched stackOverflow but all the questions do not seem to be about plain vanilla promises with the chrome extension library.
I do not want to use a plugin or library: no node.js or jquery or Q or whatever.
I have tried following the examples in the tutorials but many things do not make sense. For example, the tutorial states:
The promise constructor takes one argument—a callback with two
parameters: resolve and reject.
But then I see examples like this:
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
How this works is a mystery to me.
Also, how can you call resolve() when its never been defined? No example in the tutorials seem to match real life code. Another example is:
function isUserTooYoung(id) {
return openDatabase() // returns a promise
.then(function(col) {return find(col, {'id': id});})
How do I pass in col, or get any results!
So if anyone can give me a minimal working example of promises with an asynchronous function with its own callback, it would be greatly appreciated.
SO wants code, so here is my non-working attempt:
//loop through all
function createBookmarks(nodes, parentid){
var jlen = nodes.length;
var i;
var node;
for(var i = 0; i < nodes.length; i++){
var node = nodes[i];
createBookmark(node, parentid);
}
}
//singular create
function createBookmark(node, parentid){
var bookmark = {
parentId : parentid,
index : node['index'],
title : node['title'],
url : node['url']
}
var callback = function(result){
console.log("creation callback happened.");
return result.id; //pass ID to the callback, too
}
var promise = new Promise(function(resolve, reject) {
var newid = chrome.bookmarks.create(bookmark, callback)
if (newid){
console.log("Creating children with new id: " + newid);
resolve( createBookmarks(bookmark.children, newid));
}
});
}
//allnodes already exists
createBookmarks(allnodes[0],"0");
Just doesn't work. The result from the callback is always undefined, which it should be, and I do not see how a promise object changes anything. I am equally mystified when I try to use promise.then().
var newid = promise.then( //wait for a response?
function(result){
return chrome.bookmarks.create(bookmark, callback);
}
).catch(function(error){
console.log("error " + error);
});
if (node.children) createBookmarks(node.children, newid);
Again, newid is always undefined, because of course bookmarks.create() is asynchronous.
Thank you for any help you can offer.
Honestly, you should just use the web extension polyfill. Manually promisifying the chrome APIs is a waste of time and error prone.
If you're absolutely insistent, this is an example of how you'd promisify chrome.bookmarks.create. For other chrome.* APIs, you also have to reject the callback's error argument.
function createBookmark(bookmark) {
return new Promise(function(resolve, reject) {
try {
chrome.bookmarks.create(bookmark, function (result) {
if (chrome.runtime.lastError) reject(chrome.runtime.lastError)
else resolve(result)
})
} catch (error) {
reject(error)
}
})
}
createBookmark({})
.then(function (result) {
console.log(result)
}).catch(function (error) {
console.log(error)
})
To create multiple bookmarks, you could then:
function createBookmarks(bookmarks) {
return Promise.all(
bookmarks.map(function (bookmark) {
return createBookmark(bookmark)
})
)
}
createBookmarks([{}, {}, {}, {}])
.catch(function (error) {
console.log(error)
})
Take the advantage of the convention that the callback function always be the last argument, I use a simple helper function to promisify the chrome API:
function toPromise(api) {
return (...args) => {
return new Promise((resolve) => {
api(...args, resolve);
});
};
}
and use it like:
toPromise(chrome.bookmarks.create)(bookmark).then(...);
In my use case, it just works most of the time.
I am trying to run getResponse once when a web components finishes loading. However, when I try to run this, the debounce function just acts as an async delay and runs 4 times after 5000 ms.
static get properties() {
return {
procedure: {
type: String,
observer: 'debounce'
}
}
}
debounce() {
this._debouncer = Polymer.Debouncer.debounce(this._debouncer, Polymer.Async.timeOut.after(5000), () => {
this.getResponse();
});
}
getResponse() {
console.log('get resp');
}
What is necessary to get getResponse to run once upon the loading of the element?
Are you sure you want to use a debouncer for that? you could just use the connectedCallBack to get a one Time Event
class DemoElement extends HTMLElement {
constructor() {
super();
this.callStack = 'constructor->';
}
connectedCallback() {
this.callStack += 'connectedCallback';
console.log('rendered');
fetch(this.fakeAjax()).then((response) => {
// can't do real ajax request here so we fake it... normally you would do
// something like this.innerHTML = response.text();
// not that "rendered" get console logged before "fetch done"
this.innerHTML = `
<p>${this.callStack}</p>
<p>${response.statusText}</p>
`;
console.log('fetch done');
}).catch(function(err) {
console.log(err); // Error :(
});
}
fakeAjax() {
return window.URL.createObjectURL(new Blob(['empty']));
};
}
customElements.define('demo-element', DemoElement);
<demo-element></demo-element>
If you really need to use an observer you could also set a flag this.isLoaded in your connectedCallback() and check for that in your observer code.
const { payload: {loginType, email, password, notification, self} } = action;
console.log("--TRY--");
Firebase.login(loginType, { email, password })
.catch(function(result) {
const message =
result && result.message ? result.message : 'Sorry Some error occurs';
notification('error', message);
self.setState({
confirmLoading: false
});
isError = true;
})
.then(function(result) {
if (isError) {
return;
}
if (!result || result.message) {
const message =
result && result.message
? result.message
: 'Sorry Some error occurs';
notification('error', message);
self.setState({
confirmLoading: false
});
} else {
self.setState({
visible: false,
confirmLoading: false
});
console.log("--RIGHT BEFORE I CHECK AUTH STATE--");
//the following does NOT fire
firebaseAuth().onAuthStateChanged(function*(user) {
console.log("THE GENERATOR RUNS");
if (user) {
console.log(user);
yield put({
type: actions.LOGIN_SUCCESS,
token: 'secret token',
profile: 'Profile'
});
yield put(push('/dashboard'));
}
else {
yield put({ type: actions.LOGIN_ERROR });
}
});
}
}); });
Hi. I'm currently working with redux saga for the first time. I've been trying to get yield put to fire in the callback of the firebaseAuth().onAuthStateChanged listener. The yield keyword won't work in a function that is not an ES6 generator, so I added an asterisk to the callback but now it won't execute at all. Would really appreciate any advice on the matter.
As you noticed, redux-saga effects can only be used within a generator function, and you cannot use a generator function as a regular function: calling a generator function only returns a special object.
The right way to approach this is to use an eventChannel: it lets you connect your saga to a source of events external to the redux ecosystem.
First create your eventChannel using the provided factory function: it hands you an emit function that you can use to emit events; then consume these events using the take effect.
import { eventChannel } from 'redux-saga';
import { cancelled, take } from 'redux-saga/effects';
// first create your eventChannel
const authEventsChannel = eventChannel( emit => {
const unsubscribe = firebaseAuth().onAuthStateChanged( user => {
emit({ user });
});
// return a function that can be used to unregister listeners when the saga is cancelled
return unsubscribe;
});
// then monitor those events in your saga
try {
while (true) {
const { user } = yield take (authEventsChannel);
// handle auth state
}
} finally {
// unregister listener if the saga was cancelled
if (yield cancelled()) authEventsChannel.close();
}
Using React/Redux, I'm trying to get external data into my initial state with express. I'm used to work with D3 so I wanted to use d3.json with my reducer like this :
var url = 'http://localhost:3000/authors';
function cool() {
d3.json(url, function(data) {
dataset = data;
});
}
const authorData = (state = dataset, action) => {
switch (action.type) {
case 'DATA_CHART_ALL':
return action.data
case 'DATA_CHART_FILTER':
return action.data
default:
return state;
}
};
export default authorData;
Since D3.json is a callback function, my reducer is returned undefined. How do I deal with this ? Can I use something else than d3.json ?
You need a function that will act as a dispatcher in redux:
// redux action using a dispatcher (think middleware)
export function cool(url) {
return function(dispatch) {
return d3.json(url, response => {
dispatch(setData(response))
}
}
}
// redux action
export function setData(data) {
return {
type: 'DATA_CHART_ALL',
data
}
}
const authorDataReducer = (state = {}, action) => {
switch (action.type) {
case 'DATA_CHART_ALL':
return action.data
case 'DATA_CHART_FILTER':
return action.data
default:
return state;
}
};
export authorDataReducer;
to use it:
call this at the beginning of your application:
store.dispatch(cool("MY_URL"));
Note that i'm not checking for error handling in the request