I'm trying to do a FreeCodeCamp exercise where I call the Twitch TV API. For each channel I make a call to the API to get the channel data. I then make a subsequent call to get the streaming data. The call for the channel information is wrapped in a $.when ... then loop and seems to work fine. I then added a second call to get the stream data and code does not seem to wait for that call to complete.
$(document).ready(function() {
'use strict';
var dataArray = []; // This holds all the channels that I find. I then sort to get the most popular first.
$.when(
// TODO this should be some sort of loop to make it more flexible
getChannelData("https://api.twitch.tv/kraken/search/channels?api_version=3&q=all&limit=10&offset=0&callback=?") // First 10
).then(function() {
sortData();
displayData();
});
function getChannelData(channelStatement) {
return $.getJSON(channelStatement, function(channelData) {
channelData.channels.forEach(function(element) {
// Get stream details
var channel;
channel = {
logo: (element.logo === null) ? "https://upload.wikimedia.org/wikipedia/commons/3/33/White_square_with_question_mark.png" : element.logo, // Channel: Url for image
displayName: element.display_name, // Channel: Broadcaster name
channelName: element.name, // Channel: Channel name
url: element.url, // Channel: Used to generate link to twitch page
game: element.game, // Channel: As the name suggests
status: element.status, // Chaneel: Description of the stream
views: element.views, // Channel: As the name suggests
onLine: true
};
//dataArray.push(channel);
var streamUrl = "https://api.twitch.tv/kraken/streams/" + element.name + "?api_version=3&callback=?";
$.when(
getStreamData(streamUrl, channel)
)
.then(function() {
dataArray.push(channel);
});
}); // channel data forEach
});
}
function getStreamData(streamUrl, channel) {
return $.getJSON(streamUrl, function(stream) {
channel.onLine = (stream.stream === null) ? false : true;
});
}
function sortData() {
}
function displayData() {
}
}); // end ready
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7"
crossorigin="anonymous">
<link href='https://fonts.googleapis.com/css?family=Roboto' rel='stylesheet' type='text/css'>
<link rel="stylesheet" type="text/css" href="style.css">
<title>Twitch TV</title>
</head>
<body>
<div class="container-fluid">
<ol id="twitchList">
</ol>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<script src="TwitchTV.js"></script>
</body>
</html>
This JSBin link shows the code. Line 51 shows the second ajax call.
The code should populate an array and then on completion of all the calls display the data. I appreciate that with a production system waiting for all these calls to complete will lead to a less than idea user experience.
You need to re-oganize it slightly so that the promise you return from getChannelData is waiting on the promises created within your forEach.
function getChannelData(channelStatement) {
return $.getJSON(channelStatement).then(function(channelData) {
return $.when.apply(null, channelData.channels.map(function(element) {
// Get stream details
var channel = {
logo: (element.logo === null) ? "https://upload.wikimedia.org/wikipedia/commons/3/33/White_square_with_question_mark.png" : element.logo, // Channel: Url for image
displayName: element.display_name, // Channel: Broadcaster name
channelName: element.name, // Channel: Channel name
url: element.url, // Channel: Used to generate link to twitch page
game: element.game, // Channel: As the name suggests
status: element.status, // Chaneel: Description of the stream
views: element.views, // Channel: As the name suggests
onLine: true
};
//dataArray.push(channel);
var streamUrl = "https://api.twitch.tv/kraken/streams/" + element.name + "?api_version=3&callback=?";
return getStreamData(streamUrl, channel);
}); // channel data map
});
}
you would then use it like:
getChannelData(whatever).then(function () {
console.log(arguments); // this is dataArray
});
Related
Hi I am working in stripe and accept a payment through googlepay all my code working fine and I am accept payment.
This is my code
index.php
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Google Pay</title>
<script src="https://js.stripe.com/v3/"></script>
<?php include_once 'google.php';?>
</head>
<body>
<main>
home
<h1>Google Pay</h1>
<div id="payment-request-button">
<!-- A Stripe Element will be inserted here if the browser supports this type of payment method. -->
</div>
<div id="messages" role="alert" style="display: none;"></div>
</main>
</body>
</html>
`
this is my google.php
<?php require("stripe-php-master/init.php");
\Stripe\Stripe::setApiKey('sk_test_456'); $paymentIntent = \Stripe\PaymentIntent::create([ 'amount' => 2099, 'currency' => 'usd', ]);
?>
<script>
document.addEventListener('DOMContentLoaded', async () => {
// 1. Initialize Stripe
const stripe = Stripe("pk_test_51KdBQoC6Dz9QDrkL2stCBgcvY8936IiXUIpIjVD8n4Ug6jTs3QSEUnC9iSHrdPnSI20vxwNC6GTdEmXlBDw8UpP6005xVobO9", {
apiVersion: '2020-08-27',
});
// 2. Create a payment request object
var paymentRequest = stripe.paymentRequest({
country: 'US',
currency: 'usd',
total: {
label: 'Demo total',
amount: 1999,
},
requestPayerName: true,
requestPayerEmail: true,
});
// 3. Create a PaymentRequestButton element
const elements = stripe.elements();
const prButton = elements.create('paymentRequestButton', {
paymentRequest: paymentRequest,
});
// Check the availability of the Payment Request API,
// then mount the PaymentRequestButton
paymentRequest.canMakePayment().then(function (result) {
if (result) {
prButton.mount('#payment-request-button');
} else {
document.getElementById('payment-request-button').style.display = 'none';
addMessage('Google Pay support not found. Check the pre-requisites above and ensure you are testing in a supported browser.');
}
});
paymentRequest.on('paymentmethod', async (e) => {
// Make a call to the server to create a new
// payment intent and store its client_secret.
addMessage(`Client secret returned.`);
let clientSecret = '<?= $paymentIntent->client_secret; ?>';
// Confirm the PaymentIntent without handling potential next actions (yet).
let {error, paymentIntent} = await stripe.confirmCardPayment(
clientSecret,
{
payment_method: e.paymentMethod.id,
},
{
handleActions: false,
}
);
if (error) {
addMessage(error.message);
// Report to the browser that the payment failed, prompting it to
// re-show the payment interface, or show an error message and close
// the payment interface.
e.complete('fail');
return;
}
// Report to the browser that the confirmation was successful, prompting
// it to close the browser payment method collection interface.
e.complete('success');
// Check if the PaymentIntent requires any actions and if so let Stripe.js
// handle the flow. If using an API version older than "2019-02-11" instead
// instead check for: `paymentIntent.status === "requires_source_action"`.
if (paymentIntent.status === 'requires_action') {
// Let Stripe.js handle the rest of the payment flow.
let {error, paymentIntent} = await stripe.confirmCardPayment(
clientSecret
);
if (error) {
// The payment failed -- ask your customer for a new payment method.
addMessage(error.message);
return;
}
addMessage(`Payment ${paymentIntent.status}: ${paymentIntent.id}`);
}
addMessage(`Payment ${paymentIntent.status}: ${paymentIntent.id}`);
});
});
const addMessage = (message) => {
const messagesDiv = document.querySelector('#messages');
messagesDiv.style.display = 'block';
messagesDiv.innerHTML += '>' + message + '<br>';
console.log('StripeSampleDebug:', message);
}
</script>`
I am stuck whenever i refreshed page it charge payment incomplete and its irritating .I need that when user click on the pay button then they charge payment and it work fine but when i refreshed it charged payment and show in payment section stripe incomplete.
I'm using g-s-i a npm package to make calls to google images.
I'm unable to make this API call and, just after, render the application. I'm aware this has to do with async, but I have no idea how to use it here. I have tried many things with the function imageSearch, but nothing has worked.
I thank if someone can show me a solution to my problem.
My MWE:
HTML:
<!doctype html>
<html class="no-js" lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>Debug example</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
<!-- Place favicon.ico in the root directory -->
</head>
<body>
<h1>Debugger example</h1>
<div id="main">
</div>
</body>
<script type="module" src="./dist/bundle.js">
</script>
</html>
JS:
var gis = require('g-i-s'); // search images from google https://www.npmjs.com/package/g-i-s
var main = document.querySelector("#main")
// Query examples
var searchTopics = [
{
searchTerm: 'sleep',
queryStringAddition: '&tbs=ic:trans'
},
{
searchTerm: 'pony',
queryStringAddition: '&tbs=ic:trans',
filterOutDomains: ['deviantart.net', 'deviantart.com']
}
];
var imageSearch = (query) => {
let response = [];
gis(query, (error, results) => {
console.log(results[1].url)
response.push(results[1].url)
}
)
return response;
};
// =results= look like:
// [
// {
// "url": "https://i.ytimg.com/vi/mW3S0u8bj58/maxresdefault.jpg",
// "width": 1280,
// "height": 720
// },
// {
// "url": "https://i.ytimg.com/vi/tntOCGkgt98/maxresdefault.jpg",
// "width": 1600,
// "height": 1200
// },
// (...)
// ]
var imageLayout = (title) =>
((src) => {let e = document.createElement("div")
e.innerHTML = title
let img = document.createElement("img")
img.src = src
e.appendChild(img)
return e})
var render = (queries) => {
queries.forEach((query) =>{
let img = imageSearch(query)
console.log(img)
main.appendChild(imageLayout(query.searchTerm)(img[0]))
})}
render(searchTopics)
NPM:
npm init
npm install g-i-s
npm install --save-dev browserify #babel/core #babel/preset-env babelify
browserify js/main.js > ./dist/bundle.js -t babelify
watchify js/*.js -o ./dist/bundle.js -d
What happens in my browser:
Your imageSearch function calls gis() which doesn't successfully create the response object until the callback passed as the second parameter to gis() is called. However, you're immediately returning the empty response array.
Instead, pass a callback in to imageSearch that gets called when gis() returns, something like this:
const imageSearch = (query, cb) => {
gis(query, (error, results) => cb(error, results[1].url));
};
const render = (queries) => {
queries.forEach((query) => {
let img = imageSearch(query, (error, img) => {
if (error) {
console.log("An error happened: ", error);
return;
}
main.appendChild(imageLayout(query.searchTerm)(img))
});
});
};
You could also move on to using Promises but that's a different matter, and would not largely change the outcome of the code, just the format of it.
google-api-javascript
I was following these instructions to start using google api on an angularjs application:
quickstart
And here's my attempt:
lib/init.js
function initClient() {
console.log('-- initClient');
var resp = gapi.client.init({
apiKey: '',
clientId: '',
discoveryDocs: [
"https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest"
],
scope: [
'profile',
'https://www.googleapis.com/auth/calendar.readonly'
]
}).then(function() {
console.log('-- then') // never executes
// api loaded
}, function(error) {
console.log('ERROR: ' + error);
});
}
function handleClientLoad() {
console.log('-- handleClientLoad');
gapi.load('client:auth2', {
callback: function() {
initClient();
},
onerror: function() {
console.log('gapi failed to load!');
},
timeout: 5000, // 5 seconds.
ontimeout: function() {
// Handle timeout.
console.log('gapi.client could not load in a timely manner!');
}
});
}
index.html
<!doctype html>
<html>
<head>
<link rel="stylesheet" href="lib/style.css">
</head>
<body ng-app="my-app">
<div ng-controller="MainCtrl">
<h3>Testing Google JS API Lib</h3>
</div>
<script type="text/javascript" src="lib/init.js"></script>
<script id="gapiscript" src="https://apis.google.com/js/platform.js?onload=handleClientLoad">
</script>
<script src="lib/script.js"></script>
</body>
</html>
gapi.client.load().then() {...} is not executing and the console.log('-- then') debug line never shows. Doing document.getElementbyId('gapiscript') I get:
<script id="gapiscript" src="https://apis.google.com/…?onload=handleClientLoad" gapi_processed="true">
console.log('-- handleClientLoad'); and console.log('-- initClient'); are being executed and show in the console. I get no error message from the onerror callback.
Another console message I'm getting is CSI/start cb=gapi.loaded_0:613:127
How can I get the then() function to execute to start using the api?
In my case gapi.load accepts only single callback function:
gapi.load('client:auth2',
function() {
initClient();
}
);
I have this in my client/templates/main.html:
<head>
<title>app boil</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<script src="https://cdnjs.cloudflare.com/ajax/libs/quickblox/2.4.0/quickblox.min.js"></script>
</head>
So I am calling the quickblox api. This provides a QB object.
I have now client/templates/quickblox/quickbloxcall.js which has this code:
import { Template } from 'meteor/templating';
import './quickbloxcall.html'
Template.quickbloxcall.onRendered(function () {
console.log(QB.createSession);
});
Template.quickbloxcall.events({
'submit .quickblox-form'(event) {
var user = {
id: 4448514,
name: 'chatuserweb1',
login: 'chatuserweb1',
pass: 'chatuserweb1'
};
QB.createSession({login: user.login, password: user.pass}, function(err, res) {
if (res) {
QB.chat.connect({userId: user.id, password: user.pass}, function(err, roster) {
if (err) {
console.log(err);
} else {
/*
* (Object) roster - The user contact list
* roster = {
* '1126541': {subscription: 'both', ask: null}, // you and user with ID 1126541 subscribed to each other.
* '1126542': {subscription: 'none', ask: null}, // you don't have subscription but user maybe has
* '1126543': {subscription: 'none', ask: 'subscribe'}, // you haven't had subscription earlier but now you asked for it
* };
*/
}
});
}else{
console.log(err);
}
});
},
});
In above code, when I submit form, I get this error in console:
Uncaught TypeError: Cannot read property 'createSession' of undefined(…)
So this means that the QB object is not accessible inside Template.quickblox.events submit event handler.
However in console.log() I get this:
function (params, callback) {
this.auth.createSession(params, callback);
}
So this means that the Template.quickbloxcall.onRendered is properly loading the QB object.
How do I access this external script in Template.quickblox.events?
What you're seeing on the console does confirm that QB.createSession exists. But see that within that createSession call is a call to another createSession!
That is, I think you'll find that this.auth inside Qb.createSession is the object that's undefined, and the unavailable createSession belongs to auth (undefined), not QB (defined).
This will happen if you haven't run QB.init before calling QB.createSession. init is explained a bit in the QuickBlox JavaScript SDK docs here.
I'm using jQuery 1.7.1, Underscore 1.3.1 and Backbone 0.9.1. This is my Backbone code, in full:
$(function(){
window.Student = Backbone.Model.extend({
});
window.Students = Backbone.Collection.extend({
model: Student,
});
window.AllStudents = new Students();
AllStudents.url = "/init.json";
AllStudents.bind('reset', function() {
console.log('hello world');
});
AllStudents.fetch();
AllStudents.fetch({ url: "/init.json", success: function() {
console.log(AllStudents);
}, failure: function() {
console.log('failure');
}});
AllStudents.fetch({ url: "/init.json" }).complete(function() {
console.log(AllStudents);
});
});
Only one console statement even appears, in the third .fetch() call, and then it's an empty object.
I'm baffled. What am I doing wrong? How can I bind to the reset event, and work with the fetched data?
This is the JSON file:
[
{ text: "Amy", freq_2011: 5 },
{ text: "Angeline", freq_2011: 26 },
{ text: "Anna", freq_2011: 55 }
]
I have checked that the JSON file is being served as application/json, and I can also see that it is being fetched by the XHR request, three times.
UPDATE: This is my HTML, in full:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Test</title>
</head>
<body>
<div class="container"></div>
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.3.1/underscore-min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/backbone.js/0.9.1/backbone-min.js"></script>
<script src="/js/test.js"></script>
</body>
</html>
Can others reproduce the problem?
Backbone has a nice function which can be overridden called parse which gets the fetched data as its arguments once a successful fetch is called. You can use this to process your JSON once its loaded for instance:
window.Students = Backbone.Collection.extend({
model: Student,
parse: function(response) {
console.log(response,response.results);
return response.results;
}
});