[javascript] मैं एक एसिंक्रोनस कॉल से प्रतिक्रिया कैसे वापस कर सकता हूं?



Answers

यदि आप अपने कोड में jQuery का उपयोग नहीं कर रहे हैं, तो यह जवाब आपके लिए है

आपका कोड इस तरह के साथ कुछ होना चाहिए:

function foo() {
    var httpRequest = new XMLHttpRequest();
    httpRequest.open('GET', "/echo/json");
    httpRequest.send();
    return httpRequest.responseText;
}

var result = foo(); // always ends up being 'undefined'

फ़ेलिक्स क्लिंग ने AJAX के लिए jQuery का उपयोग करने वाले लोगों के लिए एक उत्तर लिखने का एक अच्छा काम किया है, मैंने उन लोगों के लिए एक विकल्प प्रदान करने का निर्णय लिया है जो नहीं हैं।

( नोट, नए fetch API, कोणीय या वादे का उपयोग करने वालों के लिए मैंने नीचे एक और जवाब जोड़ा है )

आप क्या सामना कर रहे हैं

यह अन्य उत्तर से "समस्या का स्पष्टीकरण" का संक्षिप्त सारांश है, यदि आप इसे पढ़ने के बाद सुनिश्चित नहीं हैं, तो इसे पढ़ें।

एजेक्स में एसिंक्रोनस के लिए खड़ा है। इसका मतलब है कि अनुरोध भेजना (या प्रतिक्रिया प्राप्त करना) सामान्य निष्पादन प्रवाह से बाहर निकाला जाता है। आपके उदाहरण में, तुरंत .send है और अगला कथन, return result; , success कॉलबैक के रूप में पारित समारोह से पहले निष्पादित किया जाता है।

इसका अर्थ यह है कि जब आप वापस आ रहे हैं, तो आपके द्वारा परिभाषित श्रोता अभी तक निष्पादित नहीं हुआ है, जिसका अर्थ है कि आप जिस मूल्य को वापस कर रहे हैं उसे परिभाषित नहीं किया गया है।

यहां एक सरल सादृश्य है

function getFive(){ 
    var a;
    setTimeout(function(){
         a=5;
    },10);
    return a;
}

(Fiddle)

a लौटाई गई मान undefined क्योंकि a=5 भाग अभी तक निष्पादित नहीं हुआ है। AJAX इस तरह कार्य करता है, सर्वर को आपके ब्राउज़र को यह बताने का मौका मिलने से पहले आप मूल्य वापस कर रहे हैं कि वह मान क्या है।

इस समस्या का एक संभावित समाधान कोड को सक्रिय रूप से कोड करना है, यह बताएं कि गणना पूरी होने पर आपके प्रोग्राम को क्या करना है।

function onComplete(a){ // When the code completes, do this
    alert(a);
}

function getFive(whenDone){ 
    var a;
    setTimeout(function(){
         a=5;
         whenDone(a);
    },10);
}

इसे CPS कहा जाता है। असल में, हम पास होने पर निष्पादित करने के लिए एक क्रिया को प्राप्त कर रहे हैं, हम एक घटना पूर्ण होने पर हमारे कोड को कैसे प्रतिक्रिया दे रहे हैं (जैसे हमारे AJAX कॉल, या इस मामले में टाइमआउट)।

उपयोग होगा:

getFive(onComplete);

स्क्रीन पर "5" को सतर्क करना चाहिए। (Fiddle)

संभव समाधान

मूल रूप से इसे हल करने के दो तरीके हैं:

  1. AJAX कॉल सिंक्रोनस बनाएं (इसे SJAX कहते हैं)।
  2. कॉलबैक के साथ ठीक से काम करने के लिए अपने कोड को पुन: व्यवस्थित करें।

1. तुल्यकालिक AJAX - ऐसा मत करो !!

सिंक्रोनस AJAX के लिए, यह मत करो! फेलिक्स का जवाब कुछ आकर्षक तर्क उठाता है कि यह एक बुरा विचार क्यों है। इसे समेकित करने के लिए, यह उपयोगकर्ता के ब्राउज़र को तब तक स्थिर कर देगा जब तक सर्वर प्रतिक्रिया न दे और बहुत खराब उपयोगकर्ता अनुभव न करे। यहां एमडीएन से लिया गया एक और संक्षिप्त सारांश क्यों है:

XMLHttpRequest सिंक्रोनस और असिंक्रोनस संचार दोनों का समर्थन करता है। सामान्य रूप से, हालांकि, प्रदर्शन कारणों से सिंक्रोनस अनुरोधों के लिए एसिंक्रोनस अनुरोधों को प्राथमिकता दी जानी चाहिए।

संक्षेप में, तुल्यकालिक अनुरोध कोड के निष्पादन को अवरुद्ध करते हैं ... ... इससे गंभीर समस्याएं हो सकती हैं ...

यदि आपको ऐसा करना है, तो आप ध्वज पास कर सकते हैं: यहां बताया गया है कि कैसे:

var request = new XMLHttpRequest();
request.open('GET', 'yourURL', false);  // `false` makes the request synchronous
request.send(null);

if (request.status === 200) {// That's HTTP for 'ok'
  console.log(request.responseText);
}

2. कोड पुनर्गठन

अपने फ़ंक्शन को कॉलबैक स्वीकार करने दें। उदाहरण कोड कोड को कॉलबैक स्वीकार करने के लिए बनाया जा सकता है। हम अपने कोड को बताएंगे कि foo पूर्ण होने पर प्रतिक्रिया कैसे करें।

इसलिए:

var result = foo();
// code that depends on `result` goes here

हो जाता है:

foo(function(result) {
    // code that depends on `result`
});

यहां हमने एक अज्ञात फ़ंक्शन पारित किया है, लेकिन हम किसी मौजूदा फ़ंक्शन के संदर्भ को आसानी से पास कर सकते हैं, जिससे यह दिखता है:

function myHandler(result) {
    // code that depends on `result`
}
foo(myHandler);

इस प्रकार के कॉलबैक डिज़ाइन को कैसे किया जाता है, इस बारे में अधिक जानकारी के लिए, फ़ेलिक्स के उत्तर की जांच करें।

अब, चलो अपने आप को तदनुसार कार्य करने के लिए परिभाषित करते हैं

function foo(callback) {
    var httpRequest = new XMLHttpRequest();
    httpRequest.onload = function(){ // when the request is loaded
       callback(httpRequest.responseText);// we're calling our method
    };
    httpRequest.open('GET', "/echo/json");
    httpRequest.send();
}

(fiddle)

हमने अब हमारे फू फ़ंक्शन को चलाने के लिए एक क्रिया को स्वीकार कर लिया है जब AJAX सफलतापूर्वक पूर्ण हो जाता है, हम यह जांच कर आगे बढ़ सकते हैं कि प्रतिक्रिया स्थिति 200 नहीं है और तदनुसार कार्य कर रही है (एक असफल हैंडलर बनाएं और ऐसे)। प्रभावी रूप से हमारे मुद्दे को हल करना।

यदि आपको अभी भी समझने में कठिनाई हो रही है तो यह पढ़ें कि एडीएक्स एमडीएन में गाइड शुरू कर रहा है

Question

मेरे पास एक फ़ंक्शन foo जो अजाक्स अनुरोध करता है। मैं foo से प्रतिक्रिया कैसे वापस कर सकता हूं?

मैंने success कॉलबैक से मूल्य वापस करने की कोशिश की और साथ ही फ़ंक्शन के अंदर स्थानीय चर के प्रति प्रतिक्रिया को आवंटित करने और उसे वापस करने का प्रयास किया, लेकिन इनमें से कोई भी तरीका वास्तव में प्रतिक्रिया वापस नहीं करता है।

function foo() {
    var result;

    $.ajax({
        url: '...',
        success: function(response) {
            result = response;
            // return response; // <- I tried that one as well
        }
    });

    return result;
}

var result = foo(); // It always ends up being `undefined`.



in node you can use deasync: https://www.npmjs.com/package/deasync

var deasync = require('deasync');
var cp = require('child_process');
var exec = deasync(cp.exec);
// output result of ls -la
try{
    console.log(exec('ls -la'));
}
catch(err){
    console.log(err);
}
// done is printed last, as supposed, with cp.exec wrapped in deasync; first without.
console.log('done');



While promises and callbacks work fine in many situations, it is a pain in the rear to express something like:

if (!name) {
  name = async1();
}
async2(name);

You'd end up going through async1 ; check if name is undefined or not and call the callback accordingly.

async1(name, callback) {
  if (name)
    callback(name)
  else {
    doSomething(callback)
  }
}

async1(name, async2)

While it is okay in small examples it gets annoying when you have a lot of similar cases and error handling involved.

Fibers helps in solving the issue.

var Fiber = require('fibers')

function async1(container) {
  var current = Fiber.current
  var result
  doSomething(function(name) {
    result = name
    fiber.run()
  })
  Fiber.yield()
  return result
}

Fiber(function() {
  var name
  if (!name) {
    name = async1()
  }
  async2(name)
  // Make any number of async calls from here
}

You can checkout the project here .




2017 answer: you can now do exactly what you want in every current browser and node 7+

This is quite simple:

  • Use the await , which will tell JavaScript to await a thing that returns a promise to be resolved
  • Add the async/await keyword to the parent function

Here's a working version of your code:

(async function(){

var response = await superagent.get('...')
console.log(response)

})()

await is supported in all current browsers and node 8




We find ourselves in a universe which appears to progress along a dimension we call "time". We don't really understand what time is, but we have developed abstractions and vocabulary that let us reason and talk about it: "past", "present", "future", "before", "after".

The computer systems we build--more and more--have time as an important dimension. Certain things are set up to happen in the future. Then other things need to happen after those first things eventually occur. This is the basic notion called "asynchronicity". In our increasingly networked world, the most common case of asynchonicity is waiting for some remote system to response to some request.

Consider an example. You call the milkman and order some milk. When it comes, you want to put it in your coffee. You can't put the milk in your coffee right now, because it is not here yet. You have to wait for it to come before putting it in your coffee. In other words, the following won't work:

var milk = order_milk();
put_in_coffee(milk);

Because JS has no way to know that it needs to wait for order_milk to finish before it executes put_in_coffee . In other words, it does not know that order_milk is asynchronous --is something that is not going to result in milk until some future time. JS, and other declarative languages, execute one statement after another without waiting.

The classic JS approach to this problem, taking advantage of the fact that JS supports functions as first-class objects which can be passed around, is to pass a function as a parameter to the asynchonous request, which it will then invoke when it has complete its task sometime in the future. That is the "callback" approach. It looks like this:

order_milk(put_in_coffee);

order_milk kicks off, orders the milk, then, when and only when it arrives, it invokes put_in_coffee .

The problem with this callback approach is that it pollutes the normal semantics of a function reporting its result with return ; instead, functions must nost reports their results by calling a callback given as a parameter. Also, this approach can rapidly become unwieldy when dealing with longer sequences of events. For example, let's say that I want to wait for the milk to be put in the coffee, and then and only then perform a third step, namely drinking the coffee. I end up needing to write something like this:

order_milk(function(milk) { put_in_coffee(milk, drink_coffee); }

where I am passing to put_in_coffee both the milk to put in it, and also the action ( drink_coffee ) to execute once the milk has been put in. Such code becomes hard to write, and read, and debug.

In this case, we could rewrite the code in the question as:

var answer;
$.ajax('/foo.json') . done(function(response) {
  callback(response.data);
});

function callback(data) {
  console.log(data);
}

Enter promises

This was the motivation for the notion of a "promise", which is a particular type of value which represents a future or asynchronous outcome of some sort. It can represent something that already happened, or that is going to happen in the future, or might never happen at all. Promises have a single method, named then , to which you pass an action to be executed when the outcome the promise represents has been realized.

In the case of our milk and coffee, we design order_milk to return a promise for the milk arriving, then specify put_in_coffee as a then action, as follows:

order_milk() . then(put_in_coffee)

One advantage of this is that we can string these together to create sequences of future occurrences ("chaining"):

order_milk() . then(put_in_coffee) . then(drink_coffee)

Let's apply promises to your particular problem. We will wrap our request logic inside a function, which returns a promise:

function get_data() {
  return $.ajax('/foo.json');
}

Actually, all we've done is added a return to the call to $.ajax . This works because jQuery's $.ajax already returns a kind of promise-like thing. (In practice, without getting into details, we would prefer to wrap this call so as return a real promise, or use some alternative to $.ajax that does so.) Now, if we want to load the file and wait for it to finish and then do something, we can simply say

get_data() . then(do_something)

for instance,

get_data() . 
  then(function(data) { console.log(data); });

When using promises, we end up passing lots of functions into then , so it's often helpful to use the more compact ES6-style arrow functions:

get_data() . 
  then(data => console.log(data));

The async keyword

But there's still something vaguely dissatisfying about having to write code one way if synchronous and a quite different way if asynchronous. For synchronous, we write

a();
b();

but if a is asynchronous, with promises we have to write

a() . then(b);

Above, we said "JS has no way to know that it needs to wait for the first call to finish before it executes the second". Wouldn't it be nice if there was some way to tell JS that? It turns out that there is--the await keyword, used inside a special type of function called an "async" function. This feature is part of the upcoming version of ES, but is already available in transpilers such as Babel given the right presets. This allows us to simply write

async function morning_routine() {
  var milk   = await order_milk();
  var coffee = await put_in_coffee(milk);
  await drink(coffee);
}

In your case, you would be able to write something like

async function foo() {
  data = await get_data();
  console.log(data);
}



Let's see the forest first before looking at the trees.

There are many informative answers with great details here, I won't repeat any of them. The key to programming in JavaScript is having first the correct mental model of overall execution.

  1. Your entry point(s) is executed as the result of an event. For example, a script tag with code is loaded into the browser. (Accordingly, this is why you may need to be concerned with the readiness of the page to run your code if it requires dom elements to be constructed first, etc.)
  2. Your code executes to completion--however many asynchronous calls it makes--without executing any of your callbacks, including XHR requests, set timeouts, dom event handlers, etc. Each of those callbacks waiting to be executed will sit in a queue, waiting their turn to be run after other events that fired have all finished execution.
  3. Each individual callback to an XHR request, set timeout or dom the event once invoked will then run to completion.

The good news is that if you understand this point well, you will never have to worry about race conditions. You should first and foremost thing of how you want to organize your code as essentially the response to different discrete events, and how you want to thread them together into a logical sequence. You can use promises or higher level new async/await as tools to that end, or you can roll your own.

But you shouldn't use any tactical tools to solve a problem until you are comfortable with the actual problem domain. Draw a map of these dependencies to know what needs to run when. Attempting an ad-hoc approach to all these callbacks is just not going to serve you well.




यदि आप वादे का उपयोग कर रहे हैं, तो यह जवाब आपके लिए है।

इसका मतलब है AngularJS, jQuery (स्थगित के साथ), देशी एक्सएचआर का प्रतिस्थापन (fetch), एम्बरजेएस, बैकबोनजेएस की बचत या कोई भी नोड लाइब्रेरी जो वादे देता है।

आपका कोड इस तरह के साथ कुछ होना चाहिए:

function foo() {
    var data;
    // or $.get(...).then, or request(...).then, or query(...).then
    fetch("/echo/json").then(function(response){
        data = response.json();
    });
    return data;
}

var result = foo(); // result is always undefined no matter what.

फ़ेलिक्स क्लिंग ने AJAX के लिए कॉलबैक के साथ jQuery का उपयोग करने वाले लोगों के लिए एक उत्तर लिखने का एक अच्छा काम किया। मेरे पास देशी एक्सएचआर का जवाब है। यह उत्तर फ्रंटेंड या बैकएंड पर या तो वादे के सामान्य उपयोग के लिए है।

मुख्य मुद्दा

ब्राउजर में जावास्क्रिप्ट समरूपता मॉडल और सर्वर पर नोडजेएस / io.js के साथ असीमित और प्रतिक्रियाशील है

जब भी आप एक ऐसी विधि को कॉल करते हैं जो वादा करता है, तो then हैंडलर हमेशा असीमित रूप से निष्पादित होते हैं - यानी, उनके नीचे दिए गए कोड के बाद .then हैंडलर में नहीं है।

इसका अर्थ यह है कि जब आप data लौट रहे हैं then आपके द्वारा परिभाषित किए गए हैंडलर को अभी तक निष्पादित नहीं किया गया है। बदले में इसका मतलब है कि आप जिस मूल्य को वापस कर रहे हैं उसे समय पर सही मूल्य पर सेट नहीं किया गया है।

इस मुद्दे के लिए यहां एक सरल सादृश्य है:

    function getFive(){
        var data;
        setTimeout(function(){ // set a timer for one second in the future
           data = 5; // after a second, do this
        }, 1000);
        return data;
    }
    document.body.innerHTML = getFive(); // `undefined` here and not 5

data का मान undefined क्योंकि data = 5 भाग अभी तक निष्पादित नहीं हुआ है। यह संभवतः एक सेकंड में निष्पादित होगा लेकिन उस समय तक यह लौटा मूल्य के लिए अप्रासंगिक है।

चूंकि ऑपरेशन अभी तक नहीं हुआ है (AJAX, सर्वर कॉल, आईओ, टाइमर) अनुरोध से पहले आप मान को वापस कर रहे हैं कि आपके कोड को यह बताने का मौका मिला कि वह मूल्य क्या है।

इस समस्या का एक संभावित समाधान कोड को सक्रिय रूप से कोड करना है, यह बताएं कि गणना पूरी होने पर आपके प्रोग्राम को क्या करना है। वादे सक्रिय रूप से प्रकृति में अस्थायी (समय-संवेदनशील) होने के द्वारा सक्रिय रूप से सक्षम करते हैं।

वादे पर त्वरित पुनरावृत्ति

एक वादा समय के साथ एक मूल्य है । वादे के पास राज्य है, वे बिना किसी मूल्य के लंबित हैं और इस पर व्यवस्थित हो सकते हैं:

  • पूरा अर्थ है कि गणना सफलतापूर्वक पूर्ण हो गई।
  • अस्वीकार कर दिया गया कि गणना विफल रही।

एक वादा केवल राज्यों को बदल सकता है जिसके बाद यह हमेशा के लिए एक ही राज्य में हमेशा रहेगा। आप then हैंडलर को अपने मूल्य निकालने और त्रुटियों को संभालने का वादा करने के लिए संलग्न कर सकते हैं। then हैंडलर कॉल की chaining की अनुमति देते हैं। वादे उन एपीआई का उपयोग करके बनाए जाते हैं जो उन्हें वापस कर देते हैं । उदाहरण के लिए, अधिक आधुनिक AJAX प्रतिस्थापन fetch या jQuery का $.get वापसी का वादा करता है।

जब हम कॉल करते हैं। .then एक वादे पर और इससे कुछ वापस लौटें - हमें संसाधित मूल्य के लिए एक वादा मिलता है। अगर हम एक और वादा वापस करते हैं तो हमें अद्भुत चीजें मिलेंगी, लेकिन चलो अपने घोड़ों को पकड़ें।

वादे के साथ

चलो देखते हैं कि हम ऊपर दिए गए मुद्दे को वादे के साथ कैसे हल कर सकते हैं। सबसे पहले, विलंब समारोह बनाने के लिए वादा कन्स्ट्रक्टर का उपयोग करके ऊपर से वादे राज्यों की हमारी समझ का प्रदर्शन करते हैं:

function delay(ms){ // takes amount of milliseconds
    // returns a new promise
    return new Promise(function(resolve, reject){
        setTimeout(function(){ // when the time is up
            resolve(); // change the promise to the fulfilled state
        }, ms);
    });
}

अब, हमने सेटटाइमआउट को वादे का उपयोग करने के लिए परिवर्तित करने के बाद, हम इसे गिनने के लिए उपयोग कर सकते हैं:

function delay(ms){ // takes amount of milliseconds
  // returns a new promise
  return new Promise(function(resolve, reject){
    setTimeout(function(){ // when the time is up
      resolve(); // change the promise to the fulfilled state
    }, ms);
  });
}

function getFive(){
  // we're RETURNING the promise, remember, a promise is a wrapper over our value
  return delay(100).then(function(){ // when the promise is ready
      return 5; // return the value 5, promises are all about return values
  })
}
// we _have_ to wrap it like this in the call site, we can't access the plain value
getFive().then(function(five){ 
   document.body.innerHTML = five;
});

Basically, instead of returning a value which we can't do because of the concurrency model - we're returning a wrapper for a value that we can unwrap with then . It's like a box you can open with then .

Applying this

This stands the same for your original API call, you can:

function foo() {
    // RETURN the promise
    return fetch("/echo/json").then(function(response){
        return response.json(); // process it inside the `then`
    });
}

foo().then(function(response){
    // access the value inside the `then`
})

So this works just as well. We've learned we can't return values from already asynchronous calls but we can use promises and chain them to perform processing. We now know how to return the response from an asynchronous call.

ES2015 (ES6)

ES6 introduces generators which are functions that can return in the middle and then resume the point they were at. This is typically useful for sequences, for example:

function* foo(){ // notice the star, this is ES6 so new browsers/node/io only
    yield 1;
    yield 2;
    while(true) yield 3;
}

Is a function that returns an iterator over the sequence 1,2,3,3,3,3,.... which can be iterated. While this is interesting on its own and opens room for a lot of possibility there is one particular interesting case.

If the sequence we're producing is a sequence of actions rather than numbers - we can pause the function whenever an action is yielded and wait for it before we resume the function. So instead of a sequence of numbers, we need a sequence of future values - that is: promises.

This somewhat tricky but very powerful trick lets us write asynchronous code in a synchronous manner. There are several "runners" that do this for you, writing one is a short few lines of code but is beyond the scope of this answer. I'll be using Bluebird's Promise.coroutine here, but there are other wrappers like co or Q.async .

var foo = coroutine(function*(){
    var data = yield fetch("/echo/json"); // notice the yield
    // code here only executes _after_ the request is done
    return data.json(); // data is defined
});

This method returns a promise itself, which we can consume from other coroutines. उदाहरण के लिए:

var main = coroutine(function*(){
   var bar = yield foo(); // wait our earlier coroutine, it returns a promise
   // server call done here, code below executes when done
   var baz = yield fetch("/api/users/"+bar.userid); // depends on foo's result
   console.log(baz); // runs after both requests done
});
main();

ES2016 (ES7)

In ES7, this is further standardized, there are several proposals right now but in all of them you can await promise. This is just "sugar" (nicer syntax) for the ES6 proposal above by adding the async and await keywords. Making the above example:

async function foo(){
    var data = await fetch("/echo/json"); // notice the await
    // code here only executes _after_ the request is done
    return data.json(); // data is defined
}

It still returns a promise just the same :)




Here are some approaches to work with asynchronous requests:

  1. then()
  2. Q - A promise library for JavaScript
  3. A+ Promises.js
  4. jQuery deferred
  5. XMLHttpRequest API
  6. Using callback concept - As implementation in first answer

Example: jQuery deferred implementation to work with multiple requests

var App = App || {};

App = {
    getDataFromServer: function(){

      var self = this,
                 deferred = $.Deferred(),
                 requests = [];

      requests.push($.getJSON('request/ajax/url/1'));
      requests.push($.getJSON('request/ajax/url/2'));

      $.when.apply(jQuery, requests).done(function(xhrResponse) {
        return deferred.resolve(xhrResponse.result);
      });
      return deferred;
    },

    init: function(){

        this.getDataFromServer().done(_.bind(function(resp1, resp2) {

           // Do the operations which you wanted to do when you
           // get a response from Ajax, for example, log response.
        }, this));
    }
};
App.init();




Use a callback() function inside the foo() success. Try in this way. It is simple and easy to understand.

var lat = "";
var lon = "";
function callback(data) {
    lat = data.lat;
    lon = data.lon;
}
function getLoc() {
    var url = "http://ip-api.com/json"
    $.getJSON(url, function(data) {
        callback(data);
    });
}

getLoc();



The following example I have written shows how to

  • Handle asynchronous HTTP calls;
  • Wait for response from each API call;
  • Use Promise pattern;
  • Use Promise.All pattern to join multiple HTTP calls;

This working example is self-contained. It will define a simple request object that uses the window XMLHttpRequest object to make calls. It will define a simple function to wait for a bunch of promises to be completed.

Context. The example is querying the Spotify Web API endpoint in order to search for playlist objects for a given set of query strings:

[
 "search?type=playlist&q=%22doom%20metal%22",
 "search?type=playlist&q=Adele"
]

For each item, a new Promise will fire a block - ExecutionBlock , parse the result, schedule a new set of promises based on the result array, that is a list of Spotify user objects and execute the new HTTP call within the ExecutionProfileBlock asynchronously.

You can then see a nested Promise structure, that lets you spawn multiple and completely asynchronous nested HTTP calls, and join the results from each subset of calls through Promise.all .

NOTE Recent Spotify search APIs will require an access token to be specified in the request headers:

-H "Authorization: Bearer {your access token}" 

So, you to run the following example you need to put your access token in the request headers:

var spotifyAccessToken = "YourSpotifyAccessToken";
var console = {
    log: function(s) {
        document.getElementById("console").innerHTML += s + "<br/>"
    }
}

// Simple XMLHttpRequest
// based on https://davidwalsh.name/xmlhttprequest
SimpleRequest = {
    call: function(what, response) {
        var request;
        if (window.XMLHttpRequest) { // Mozilla, Safari, ...
            request = new XMLHttpRequest();
        } else if (window.ActiveXObject) { // Internet Explorer
            try {
                request = new ActiveXObject('Msxml2.XMLHTTP');
            }
            catch (e) {
                try {
                  request = new ActiveXObject('Microsoft.XMLHTTP');
                } catch (e) {}
            }
        }

        // State changes
        request.onreadystatechange = function() {
            if (request.readyState === 4) { // Done
                if (request.status === 200) { // Complete
                    response(request.responseText)
                }
                else
                    response();
            }
        }
        request.open('GET', what, true);
        request.setRequestHeader("Authorization", "Bearer " + spotifyAccessToken);
        request.send(null);
    }
}

//PromiseAll
var promiseAll = function(items, block, done, fail) {
    var self = this;
    var promises = [],
                   index = 0;
    items.forEach(function(item) {
        promises.push(function(item, i) {
            return new Promise(function(resolve, reject) {
                if (block) {
                    block.apply(this, [item, index, resolve, reject]);
                }
            });
        }(item, ++index))
    });
    Promise.all(promises).then(function AcceptHandler(results) {
        if (done) done(results);
    }, function ErrorHandler(error) {
        if (fail) fail(error);
    });
}; //promiseAll

// LP: deferred execution block
var ExecutionBlock = function(item, index, resolve, reject) {
    var url = "https://api.spotify.com/v1/"
    url += item;
    console.log( url )
    SimpleRequest.call(url, function(result) {
        if (result) {

            var profileUrls = JSON.parse(result).playlists.items.map(function(item, index) {
                return item.owner.href;
            })
            resolve(profileUrls);
        }
        else {
            reject(new Error("call error"));
        }
    })
}

arr = [
    "search?type=playlist&q=%22doom%20metal%22",
    "search?type=playlist&q=Adele"
]

promiseAll(arr, function(item, index, resolve, reject) {
    console.log("Making request [" + index + "]")
    ExecutionBlock(item, index, resolve, reject);
}, function(results) { // Aggregated results

    console.log("All profiles received " + results.length);
    //console.log(JSON.stringify(results[0], null, 2));

    ///// promiseall again

    var ExecutionProfileBlock = function(item, index, resolve, reject) {
        SimpleRequest.call(item, function(result) {
            if (result) {
                var obj = JSON.parse(result);
                resolve({
                    name: obj.display_name,
                    followers: obj.followers.total,
                    url: obj.href
                });
            } //result
        })
    } //ExecutionProfileBlock

    promiseAll(results[0], function(item, index, resolve, reject) {
        //console.log("Making request [" + index + "] " + item)
        ExecutionProfileBlock(item, index, resolve, reject);
    }, function(results) { // aggregated results
        console.log("All response received " + results.length);
        console.log(JSON.stringify(results, null, 2));
    }

    , function(error) { // Error
        console.log(error);
    })

    /////

  },
  function(error) { // Error
      console.log(error);
  });
<div id="console" />

I have extensively discussed this solution here .




Angular1

For people who are using AngularJS , can handle this situation using Promises .

Here it says,

Promises can be used to unnest asynchronous functions and allows one to chain multiple functions together.

You can find a nice explanation here also.

Example found in docs mentioned below.

  promiseB = promiseA.then(
    function onSuccess(result) {
      return result + 1;
    }
    ,function onError(err) {
      //Handle error
    }
  );

 // promiseB will be resolved immediately after promiseA is resolved 
 // and its value will be the result of promiseA incremented by 1.

Angular2 and Later

In Angular2 with look at the following example, but its recommended to use Observables with Angular2 .

 search(term: string) {
     return this.http
  .get(`https://api.spotify.com/v1/search?q=${term}&type=artist`)
  .map((response) => response.json())
  .toPromise();

}

You can consume that in this way,

search() {
    this.searchService.search(this.searchField.value)
      .then((result) => {
    this.result = result.artists.items;
  })
  .catch((error) => console.error(error));
}

See the original post here. But Typescript does not support native es6 Promises , if you want to use it, you might need plugin for that.

Additionally here is the promises spec define here.




The simplest solution is create a JavaScript function and call it for the Ajax success callback.

function callServerAsync(){
    $.ajax({
        url: '...',
        success: function(response) {

            successCallback(response);
        }
    });
}

function successCallback(responseObj){
    // Do something like read the response and show data
    alert(JSON.stringify(responseObj)); // Only applicable to JSON response
}

function foo(callback) {

    $.ajax({
        url: '...',
        success: function(response) {
           return callback(null, response);
        }
    });
}

var result = foo(function(err, result){
          if (!err)
           console.log(result);    
}); 



ECMAScript 6 has 'generators' which allow you to easily program in an asynchronous style.

function* myGenerator() {
    let response = yield $.ajax("https://.com", {complete: asyncCallback});
    console.log("response is:", response);

    // examples of other things you can do
    yield setTimeout(asyncCallback, 1000);
    console.log("it delayed for 1000ms");
    while (response.statusText === "error") {
        response = yield* anotherGenerator();
    }
}

To run the above code you do this:

const gen = myGenerator();
const asyncCallback = (...args) => gen.next(...args);
asyncCallback();

If you need to target browsers that don't support ES6 you can run the code through Babel or closure-compiler to generate ECMAScript 5.




Have a look at this example:

var app = angular.module('plunker', []);

app.controller('MainCtrl', function($scope,$http) {

    var getJoke = function(){
        return $http.get('http://api.icndb.com/jokes/random').then(function(res){
            return res.data.value;  
        });
    }

    getJoke().then(function(res) {
        console.log(res.joke);
    });
});

As you can see getJoke is returning a resolved promise (it is resolved when returning res.data.value ). So you wait until the $http.get request is completed and then console.log(res.joke) is executed (as a normal asynchronous flow).

This is the plnkr:

http://embed.plnkr.co/XlNR7HpCaIhJxskMJfSg/




You can use this custom library (written using Promise) to make a remote call.

function $http(apiConfig) {
    return new Promise(function (resolve, reject) {
        var client = new XMLHttpRequest();
        client.open(apiConfig.method, apiConfig.url);
        client.send();
        client.onload = function () {
            if (this.status >= 200 && this.status < 300) {
                // Performs the function "resolve" when this.status is equal to 2xx.
                // Your logic here.
                resolve(this.response);
            }
            else {
                // Performs the function "reject" when this.status is different than 2xx.
                reject(this.statusText);
            }
        };
        client.onerror = function () {
            reject(this.statusText);
        };
    });
}

Simple usage example:

$http({
    method: 'get',
    url: 'google.com'
}).then(function(response) {
    console.log(response);
}, function(error) {
    console.log(error)
});



Related