How do I return the response/result from a function foo
that makes an asynchronous request?
I am trying to return the value from the callback, as well as assigning the result to a local variable inside the function and returning that one, but none of those ways actually return the response — they all return undefined
or whatever the initial value of the variable result
is.
Example of an asynchronous function that accepts a callback (using jQuery’s ajax
function):
function foo() {
var result;
$.ajax({
url: '...',
success: function(response) {
result = response;
// return response; // <- I tried that one as well
}
});
return result; // It always returns `undefined`
}
Example using Node.js:
function foo() {
var result;
fs.readFile("path/to/file", function(err, data) {
result = data;
// return data; // <- I tried that one as well
});
return result; // It always returns `undefined`
}
Example using the then
block of a promise:
function foo() {
var result;
fetch(url).then(function(response) {
result = response;
// return response; // <- I tried that one as well
});
return result; // It always returns `undefined`
}
4
→ For a more general explanation of asynchronous behaviour with different examples, see Why is my variable unaltered after I modify it inside of a function? – Asynchronous code reference
→ If you already understand the problem, skip to the possible solutions below.
The problem
The A in Ajax stands for asynchronous. That means sending the request (or rather receiving the response) is taken out of the normal execution flow. In your example, $.ajax
returns immediately and the next statement, return result;
, is executed before the function you passed as success
callback was even called.
Here is an analogy which hopefully makes the difference between synchronous and asynchronous flow clearer:
Synchronous
Imagine you make a phone call to a friend and ask him to look something up for you. Although it might take a while, you wait on the phone and stare into space, until your friend gives you the answer that you needed.
The same is happening when you make a function call containing “normal” code:
function findItem() {
var item;
while(item_not_found) {
// search
}
return item;
}
var item = findItem();
// Do something with item
doSomethingElse();
Even though findItem
might take a long time to execute, any code coming after var item = findItem();
has to wait until the function returns the result.
Asynchronous
You call your friend again for the same reason. But this time you tell him that you are in a hurry and he should call you back on your mobile phone. You hang up, leave the house, and do whatever you planned to do. Once your friend calls you back, you are dealing with the information he gave to you.
That’s exactly what’s happening when you do an Ajax request.
findItem(function(item) {
// Do something with the item
});
doSomethingElse();
Instead of waiting for the response, the execution continues immediately and the statement after the Ajax call is executed. To get the response eventually, you provide a function to be called once the response was received, a callback (notice something? call back ?). Any statement coming after that call is executed before the callback is called.
Solution(s)
Embrace the asynchronous nature of JavaScript! While certain asynchronous operations provide synchronous counterparts (so does “Ajax”), it’s generally discouraged to use them, especially in a browser context.
Why is it bad do you ask?
JavaScript runs in the UI thread of the browser and any long-running process will lock the UI, making it unresponsive. Additionally, there is an upper limit on the execution time for JavaScript and the browser will ask the user whether to continue the execution or not.
All of this results in a really bad user experience. The user won’t be able to tell whether everything is working fine or not. Furthermore, the effect will be worse for users with a slow connection.
In the following we will look at three different solutions that are all building on top of each other:
- Promises with
async/await
(ES2017+, available in older browsers if you use a transpiler or regenerator) - Callbacks (popular in node)
- Promises with
then()
(ES2015+, available in older browsers if you use one of the many promise libraries)
All three are available in current browsers, and node 7+.
ES2017+: Promises with async/await
The ECMAScript version released in 2017 introduced syntax-level support for asynchronous functions. With the help of async
and await
, you can write asynchronous in a “synchronous style”. The code is still asynchronous, but it’s easier to read/understand.
async/await
builds on top of promises: an async
function always returns a promise. await
“unwraps” a promise and either result in the value the promise was resolved with or throws an error if the promise was rejected.
Important: You can only use await
inside an async
function or in a JavaScript module. Top-level await
is not supported outside of modules, so you might have to make an async IIFE (Immediately Invoked Function Expression) to start an async
context if not using a module.
You can read more about async
and await
on MDN.
Here is an example that elaborates the delay function findItem()
above:
// Using 'superagent' which will return a promise.
var superagent = require('superagent')
// This is isn't declared as `async` because it already returns a promise
function delay() {
// `delay` returns a promise
return new Promise(function(resolve, reject) {
// Only `delay` is able to resolve or reject the promise
setTimeout(function() {
resolve(42); // After 3 seconds, resolve the promise with value 42
}, 3000);
});
}
async function getAllBooks() {
try {
// GET a list of book IDs of the current user
var bookIDs = await superagent.get('/user/books');
// wait for 3 seconds (just for the sake of this example)
await delay();
// GET information about each book
return superagent.get('/books/ids='+JSON.stringify(bookIDs));
} catch(error) {
// If any of the awaited promises was rejected, this catch block
// would catch the rejection reason
return null;
}
}
// Start an IIFE to use `await` at the top level
(async function(){
let books = await getAllBooks();
console.log(books);
})();
Current browser and node versions support async/await
. You can also support older environments by transforming your code to ES5 with the help of regenerator (or tools that use regenerator, such as Babel).
Let functions accept callbacks
A callback is when function 1 is passed to function 2. Function 2 can call function 1 whenever it is ready. In the context of an asynchronous process, the callback will be called whenever the asynchronous process is done. Usually, the result is passed to the callback.
In the example of the question, you can make foo
accept a callback and use it as success
callback. So this
var result = foo();
// Code that depends on 'result'
becomes
foo(function(result) {
// Code that depends on 'result'
});
Here we defined the function “inline” but you can pass any function reference:
function myCallback(result) {
// Code that depends on 'result'
}
foo(myCallback);
foo
itself is defined as follows:
function foo(callback) {
$.ajax({
// ...
success: callback
});
}
callback
will refer to the function we pass to foo
when we call it and we pass it on to success
. I.e. once the Ajax request is successful, $.ajax
will call callback
and pass the response to the callback (which can be referred to with result
, since this is how we defined the callback).
You can also process the response before passing it to the callback:
function foo(callback) {
$.ajax({
// ...
success: function(response) {
// For example, filter the response
callback(filtered_response);
}
});
}
It’s easier to write code using callbacks than it may seem. After all, JavaScript in the browser is heavily event-driven (DOM events). Receiving the Ajax response is nothing else but an event.
Difficulties could arise when you have to work with third-party code, but most problems can be solved by just thinking through the application flow.
ES2015+: Promises with then()
The Promise API is a new feature of ECMAScript 6 (ES2015), but it has good browser support already. There are also many libraries which implement the standard Promises API and provide additional methods to ease the use and composition of asynchronous functions (e.g., bluebird).
Promises are containers for future values. When the promise receives the value (it is resolved) or when it is canceled (rejected), it notifies all of its “listeners” who want to access this value.
The advantage over plain callbacks is that they allow you to decouple your code and they are easier to compose.
Here is an example of using a promise:
function delay() {
// `delay` returns a promise
return new Promise(function(resolve, reject) {
// Only `delay` is able to resolve or reject the promise
setTimeout(function() {
resolve(42); // After 3 seconds, resolve the promise with value 42
}, 3000);
});
}
delay()
.then(function(v) { // `delay` returns a promise
console.log(v); // Log the value once it is resolved
})
.catch(function(v) {
// Or do something else if it is rejected
// (it would not happen in this example, since `reject` is not called).
});
.as-console-wrapper { max-height: 100% !important; top: 0; }
Applied to our Ajax call we could use promises like this:
function ajax(url) {
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.onload = function() {
resolve(this.responseText);
};
xhr.onerror = reject;
xhr.open('GET', url);
xhr.send();
});
}
ajax("https://jsonplaceholder.typicode.com/todos/1")
.then(function(result) {
console.log(result); // Code depending on result
})
.catch(function() {
// An error occurred
});
.as-console-wrapper { max-height: 100% !important; top: 0; }
Describing all the advantages that promise offer is beyond the scope of this answer, but if you write new code, you should seriously consider them. They provide a great abstraction and separation of your code.
More information about promises: HTML5 rocks – JavaScript Promises.
Side note: jQuery’s deferred objects
Deferred objects are jQuery’s custom implementation of promises (before the Promise API was standardized). They behave almost like promises but expose a slightly different API.
Every Ajax method of jQuery already returns a “deferred object” (actually a promise of a deferred object) which you can just return from your function:
function ajax() {
return $.ajax(...);
}
ajax().done(function(result) {
// Code depending on result
}).fail(function() {
// An error occurred
});
Side note: Promise gotchas
Keep in mind that promises and deferred objects are just containers for a future value, they are not the value itself. For example, suppose you had the following:
function checkPassword() {
return $.ajax({
url: '/password',
data: {
username: $('#username').val(),
password: $('#password').val()
},
type: 'POST',
dataType: 'json'
});
}
if (checkPassword()) {
// Tell the user they're logged in
}
This code misunderstands the above asynchronous issues. Specifically, $.ajax()
doesn’t freeze the code while it checks the ‘/password’ page on your server – it sends a request to the server and while it waits, it immediately returns a jQuery Ajax Deferred object, not the response from the server. That means the if
statement is going to always get this Deferred object, treat it as true
, and proceed as though the user is logged in. Not good.
But the fix is easy:
checkPassword()
.done(function(r) {
if (r) {
// Tell the user they're logged in
} else {
// Tell the user their password was bad
}
})
.fail(function(x) {
// Tell the user something bad happened
});
Not recommended: Synchronous “Ajax” calls
As I mentioned, some(!) asynchronous operations have synchronous counterparts. I don’t advocate their use, but for completeness’ sake, here is how you would perform a synchronous call:
Without jQuery
If you directly use a XMLHttpRequest
object, pass false
as third argument to .open
.
jQuery
If you use jQuery, you can set the async
option to false
. Note that this option is deprecated since jQuery 1.8.
You can then either still use a success
callback or access the responseText
property of the jqXHR object:
function foo() {
var jqXHR = $.ajax({
//...
async: false
});
return jqXHR.responseText;
}
If you use any other jQuery Ajax method, such as $.get
, $.getJSON
, etc., you have to change it to $.ajax
(since you can only pass configuration parameters to $.ajax
).
Heads up! It is not possible to make a synchronous JSONP request. JSONP by its very nature is always asynchronous (one more reason to not even consider this option).
17
If you’re not using jQuery in your code, this answer is for you
Your code should be something along the lines of this:
function foo() {
var httpRequest = new XMLHttpRequest();
httpRequest.open('GET', "/echo/json");
httpRequest.send();
return httpRequest.responseText;
}
var result = foo(); // Always ends up being 'undefined'
Felix Kling did a fine job writing an answer for people using jQuery for AJAX, but I’ve decided to provide an alternative for people who aren’t.
(Note, for those using the new fetch
API, Angular or promises I’ve added another answer below)
What you’re facing
This is a short summary of “Explanation of the problem” from the other answer, if you’re not sure after reading this, read that.
The A in AJAX stands for asynchronous. That means sending the request (or rather receiving the response) is taken out of the normal execution flow. In your example, .send
returns immediately and the next statement, return result;
, is executed before the function you passed as success
callback was even called.
This means when you’re returning, the listener you’ve defined did not execute yet, which means the value you’re returning has not been defined.
Here is a simple analogy:
function getFive(){
var a;
setTimeout(function(){
a=5;
},10);
return a;
}
(Fiddle)
The value of a
returned is undefined
since the a=5
part has not executed yet. AJAX acts like this, you’re returning the value before the server got the chance to tell your browser what that value is.
One possible solution to this problem is to code re-actively , telling your program what to do when the calculation completed.
function onComplete(a){ // When the code completes, do this
alert(a);
}
function getFive(whenDone){
var a;
setTimeout(function(){
a=5;
whenDone(a);
},10);
}
This is called CPS. Basically, we’re passing getFive
an action to perform when it completes, we’re telling our code how to react when an event completes (like our AJAX call, or in this case the timeout).
Usage would be:
getFive(onComplete);
Which should alert “5” to the screen. (Fiddle).
Possible solutions
There are basically two ways how to solve this:
- Make the AJAX call synchronous (let’s call it SJAX).
- Restructure your code to work properly with callbacks.
1. Synchronous AJAX – Don’t do it!!
As for synchronous AJAX, don’t do it! Felix’s answer raises some compelling arguments about why it’s a bad idea. To sum it up, it’ll freeze the user’s browser until the server returns the response and create a very bad user experience. Here is another short summary taken from MDN on why:
XMLHttpRequest supports both synchronous and asynchronous communications. In general, however, asynchronous requests should be preferred to synchronous requests for performance reasons.
In short, synchronous requests block the execution of code… …this can cause serious issues…
If you have to do it, you can pass a flag. Here is how:
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. Restructure code
Let your function accept a callback. In the example code foo
can be made to accept a callback. We’ll be telling our code how to react when foo
completes.
So:
var result = foo();
// Code that depends on `result` goes here
Becomes:
foo(function(result) {
// Code that depends on `result`
});
Here we passed an anonymous function, but we could just as easily pass a reference to an existing function, making it look like:
function myHandler(result) {
// Code that depends on `result`
}
foo(myHandler);
For more details on how this sort of callback design is done, check Felix’s answer.
Now, let’s define foo itself to act accordingly
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)
We have now made our foo function accept an action to run when the AJAX completes successfully. We can extend this further by checking if the response status is not 200 and acting accordingly (create a fail handler and such). Effectively it is solving our issue.
If you’re still having a hard time understanding this, read the AJAX getting started guide at MDN.
2
XMLHttpRequest 2 (first of all, read the answers from Benjamin Gruenbaum and Felix Kling)
If you don’t use jQuery and want a nice short XMLHttpRequest 2 which works in modern browsers and also in mobile browsers, I suggest using it this way:
function ajax(a, b, c){ // URL, callback, just a placeholder
c = new XMLHttpRequest;
c.open('GET', a);
c.onload = b;
c.send()
}
As you can see:
- It’s shorter than all other functions Listed.
- The callback is set directly (so no extra unnecessary closures).
- It uses the new onload (so you don’t have to check for readystate && status)
- There are some other situations, which I don’t remember, that make the XMLHttpRequest 1 annoying.
There are two ways to get the response of this Ajax call (three using the XMLHttpRequest var name):
The simplest:
this.response
Or if for some reason you bind()
the callback to a class:
e.target.response
Example:
function callback(e){
console.log(this.response);
}
ajax('URL', callback);
Or (the above one is better anonymous functions are always a problem):
ajax('URL', function(e){console.log(this.response)});
Nothing easier.
Now some people will probably say that it’s better to use onreadystatechange or even the XMLHttpRequest variable name. That’s wrong.
Check out XMLHttpRequest advanced features.
It supported all *modern browsers. And I can confirm as I have been using this approach since XMLHttpRequest 2 was created. I never had any type of problem in any browsers I used.
onreadystatechange is only useful if you want to get the headers on state 2.
Using the XMLHttpRequest
variable name is another big error as you need to execute the callback inside the onload/oreadystatechange closures, or else you lost it.
Now if you want something more complex using POST and FormData you can easily extend this function:
function x(a, b, e, d, c){ // URL, callback, method, formdata or {key:val},placeholder
c = new XMLHttpRequest;
c.open(e||'get', a);
c.onload = b;
c.send(d||null)
}
Again … it’s a very short function, but it does GET and POST.
Examples of usage:
x(url, callback); // By default it's GET so no need to set
x(url, callback, 'post', {'key': 'val'}); // No need to set POST data
Or pass a full form element (document.getElementsByTagName('form')[0]
):
var fd = new FormData(form);
x(url, callback, 'post', fd);
Or set some custom values:
var fd = new FormData();
fd.append('key', 'val')
x(url, callback, 'post', fd);
As you can see, I didn’t implement sync… it’s a bad thing.
Having said that … why don’t we do it the easy way?
As mentioned in the comment, the use of error && synchronous does completely break the point of the answer. Which is a nice short way to use Ajax properly?
Error handler
function x(a, b, e, d, c){ // URL, callback, method, formdata or {key:val}, placeholder
c = new XMLHttpRequest;
c.open(e||'get', a);
c.onload = b;
c.onerror = error;
c.send(d||null)
}
function error(e){
console.log('--Error--', this.type);
console.log('this: ', this);
console.log('Event: ', e)
}
function displayAjax(e){
console.log(e, this);
}
x('WRONGURL', displayAjax);
In the above script, you have an error handler which is statically defined, so it does not compromise the function. The error handler can be used for other functions too.
But to get out an error, the only way is to write a wrong URL in which case every browser throws an error.
Error handlers may maybe useful if you set custom headers, set the responseType to blob array buffer, or whatever…
Even if you pass ‘POSTAPAPAP’ as the method it won’t throw an error.
Even if you pass ‘fdggdgilfdghfldj’ as formdata it won’t throw an error.
In the first case, the error is inside the displayAjax()
under this.statusText
as Method not Allowed
.
In the second case, it simply works. You have to check on the server side if you passed the right post data.
Cross-domain not allowed throws an error automatically.
In the error response, there aren’t any error codes.
There is only the this.type
which is set to error.
Why add an error handler if you don’t have any control over errors?
Most of the errors are returned inside this in the callback function displayAjax()
.
So: There isn’t any need for error checks if you’re able to copy and paste the URL properly. 😉
PS: As the first test I wrote x(‘x’, displayAjax)…, and it totally got a response…??? So I checked the folder where the HTML is located, and there was a file called ‘x.xml’. So even if you forget the extension of your file XMLHttpRequest 2 WILL FIND IT. I LOL’d
Read a file synchronous
Don’t do that.
If you want to block the browser for a while load a nice big .txt
file synchronous.
function omg(a, c){ // URL
c = new XMLHttpRequest;
c.open('GET', a, true);
c.send();
return c; // Or c.response
}
Now you can do
var res = omg('thisIsGonnaBlockThePage.txt');
There is no other way to do this in a non-asynchronous way. (Yeah, with setTimeout loop… but seriously?)
Another point is… if you work with APIs or just your own list’s files or whatever you always use different functions for each request…
Only if you have a page where you load always the same XML/JSON or whatever you need only one function. In that case, modify a little the Ajax function and replace b with your special function.
The functions above are for basic use.
If you want to extend the function…
Yes, you can.
I’m using a lot of APIs and one of the first functions I integrate into every HTML page is the first Ajax function in this answer, with GET only…
But you can do a lot of stuff with XMLHttpRequest 2:
I made a download manager (using ranges on both sides with resume, filereader, and filesystem), various image resizers converters using canvas, populate web SQL databases with base64images and much more…
But in these cases you should create a function only for that purpose… sometimes you need a blob, or array buffers, you can set headers, override mimetype and there is a lot more…
But the question here is how to return an Ajax response… (I added an easy way.)
3
If you’re using promises, this answer is for you.
This means AngularJS, jQuery (with deferred), native XHR’s replacement (fetch), Ember.js, Backbone.js’s save or any Node.js library that returns promises.
Your code should be something along the lines of this:
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.
Felix Kling did a fine job writing an answer for people using jQuery with callbacks for Ajax. I have an answer for native XHR. This answer is for generic usage of promises either on the frontend or backend.
The core issue
The JavaScript concurrency model in the browser and on the server with Node.js/io.js is asynchronous and reactive.
Whenever you call a method that returns a promise, the then
handlers are always executed asynchronously – that is, after the code below them that is not in a .then
handler.
This means when you’re returning data
the then
handler you’ve defined did not execute yet. This in turn means that the value you’re returning has not been set to the correct value in time.
Here is a simple analogy for the issue:
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
The value of data
is undefined
since the data = 5
part has not executed yet. It will likely execute in a second, but by that time it is irrelevant to the returned value.
Since the operation did not happen yet (Ajax, server call, I/O, and timer) you’re returning the value before the request got the chance to tell your code what that value is.
One possible solution to this problem is to code re-actively, telling your program what to do when the calculation completed. Promises actively enable this by being temporal (time-sensitive) in nature.
Quick recap on promises
A Promise is a value over time. Promises have state. They start as pending with no value and can settle to:
- fulfilled meaning that the computation completed successfully.
- rejected meaning that the computation failed.
A promise can only change states once after which it will always stay at the same state forever. You can attach then
handlers to promises to extract their value and handle errors. then
handlers allow chaining of calls. Promises are created by using APIs that return them. For example, the more modern Ajax replacement fetch
or jQuery’s $.get
return promises.
When we call .then
on a promise and return something from it – we get a promise for the processed value. If we return another promise we’ll get amazing things, but let’s hold our horses.
With promises
Let’s see how we can solve the above issue with promises. First, let’s demonstrate our understanding of promise states from above by using the Promise constructor for creating a delay function:
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);
});
}
Now, after we converted setTimeout to use promises, we can use then
to make it count:
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, and 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, Nodes.js, and io.js 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 a tricky, but very powerful trick let’s 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 it 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
// The 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. For example:
var main = coroutine(function*(){
var bar = yield foo(); // Wait our earlier coroutine. It returns a promise
// The server call is done here, and the 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 are 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 🙂
1
You are using Ajax incorrectly. The idea is not to have it return anything, but instead hand off the data to something called a callback function, which handles the data.
That is:
function handleData( responseData ) {
// Do what you want with the data
console.log(responseData);
}
$.ajax({
url: "hi.php",
...
success: function ( data, status, XHR ) {
handleData(data);
}
});
Returning anything in the submit handler will not do anything. You must instead either hand off the data, or do what you want with it directly inside the success function.
1
I will answer with a horrible-looking, hand-drawn comic. The second image is the reason why result
is undefined
in your code example.
6
The simplest solution is to 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 a 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);
});
3
Most of the answers here give useful suggestions for when you have a single async operation, but sometimes, this comes up when you need to do an asynchronous operation for each entry in an array or other list-like structure. The temptation is to do this:
// WRONG
var results = [];
theArray.forEach(function(entry) {
doSomethingAsync(entry, function(result) {
results.push(result);
});
});
console.log(results); // E.g., using them, returning them, etc.
Example:
// WRONG
var theArray = [1, 2, 3];
var results = [];
theArray.forEach(function(entry) {
doSomethingAsync(entry, function(result) {
results.push(result);
});
});
console.log("Results:", results); // E.g., using them, returning them, etc.
function doSomethingAsync(value, callback) {
console.log("Starting async operation for " + value);
setTimeout(function() {
console.log("Completing async operation for " + value);
callback(value * 2);
}, Math.floor(Math.random() * 200));
}
.as-console-wrapper { max-height: 100% !important; }
The reason that doesn’t work is that the callbacks from doSomethingAsync
haven’t run yet by the time you’re trying to use the results.
So, if you have an array (or list of some kind) and want to do async operations for each entry, you have two options: Do the operations in parallel (overlapping), or in series (one after another in sequence).
Parallel
You can start all of them and keep track of how many callbacks you’re expecting, and then use the results when you’ve gotten that many callbacks:
var results = [];
var expecting = theArray.length;
theArray.forEach(function(entry, index) {
doSomethingAsync(entry, function(result) {
results[index] = result;
if (--expecting === 0) {
// Done!
console.log("Results:", results); // E.g., using the results
}
});
});
Example:
var theArray = [1, 2, 3];
var results = [];
var expecting = theArray.length;
theArray.forEach(function(entry, index) {
doSomethingAsync(entry, function(result) {
results[index] = result;
if (--expecting === 0) {
// Done!
console.log("Results:", JSON.stringify(results)); // E.g., using the results
}
});
});
function doSomethingAsync(value, callback) {
console.log("Starting async operation for " + value);
setTimeout(function() {
console.log("Completing async operation for " + value);
callback(value * 2);
}, Math.floor(Math.random() * 200));
}
.as-console-wrapper { max-height: 100% !important; }
(We could do away with expecting
and just use results.length === theArray.length
, but that leaves us open to the possibility that theArray
is changed while the calls are outstanding…)
Notice how we use the index
from forEach
to save the result in results
in the same position as the entry it relates to, even if the results arrive out of order (since async calls don’t necessarily complete in the order in which they were started).
But what if you need to return those results from a function? As the other answers have pointed out, you can’t; you have to have your function accept and call a callback (or return a Promise). Here’s a callback version:
function doSomethingWith(theArray, callback) {
var results = [];
var expecting = theArray.length;
theArray.forEach(function(entry, index) {
doSomethingAsync(entry, function(result) {
results[index] = result;
if (--expecting === 0) {
// Done!
callback(results);
}
});
});
}
doSomethingWith(theArray, function(results) {
console.log("Results:", results);
});
Example:
function doSomethingWith(theArray, callback) {
var results = [];
var expecting = theArray.length;
theArray.forEach(function(entry, index) {
doSomethingAsync(entry, function(result) {
results[index] = result;
if (--expecting === 0) {
// Done!
callback(results);
}
});
});
}
doSomethingWith([1, 2, 3], function(results) {
console.log("Results:", JSON.stringify(results));
});
function doSomethingAsync(value, callback) {
console.log("Starting async operation for " + value);
setTimeout(function() {
console.log("Completing async operation for " + value);
callback(value * 2);
}, Math.floor(Math.random() * 200));
}
.as-console-wrapper { max-height: 100% !important; }
Or here’s a version returning a Promise
instead:
function doSomethingWith(theArray) {
return new Promise(function(resolve) {
var results = [];
var expecting = theArray.length;
theArray.forEach(function(entry, index) {
doSomethingAsync(entry, function(result) {
results[index] = result;
if (--expecting === 0) {
// Done!
resolve(results);
}
});
});
});
}
doSomethingWith(theArray).then(function(results) {
console.log("Results:", results);
});
Of course, if doSomethingAsync
passed us errors, we’d use reject
to reject the promise when we got an error.)
Example:
function doSomethingWith(theArray) {
return new Promise(function(resolve) {
var results = [];
var expecting = theArray.length;
theArray.forEach(function(entry, index) {
doSomethingAsync(entry, function(result) {
results[index] = result;
if (--expecting === 0) {
// Done!
resolve(results);
}
});
});
});
}
doSomethingWith([1, 2, 3]).then(function(results) {
console.log("Results:", JSON.stringify(results));
});
function doSomethingAsync(value, callback) {
console.log("Starting async operation for " + value);
setTimeout(function() {
console.log("Completing async operation for " + value);
callback(value * 2);
}, Math.floor(Math.random() * 200));
}
.as-console-wrapper { max-height: 100% !important; }
(Or alternately, you could make a wrapper for doSomethingAsync
that returns a promise, and then do the below…)
If doSomethingAsync
gives you a Promise, you can use Promise.all
:
function doSomethingWith(theArray) {
return Promise.all(theArray.map(function(entry) {
return doSomethingAsync(entry);
}));
}
doSomethingWith(theArray).then(function(results) {
console.log("Results:", results);
});
If you know that doSomethingAsync
will ignore a second and third argument, you can just pass it directly to map
(map
calls its callback with three arguments, but most people only use the first most of the time):
function doSomethingWith(theArray) {
return Promise.all(theArray.map(doSomethingAsync));
}
doSomethingWith(theArray).then(function(results) {
console.log("Results:", results);
});
Example:
function doSomethingWith(theArray) {
return Promise.all(theArray.map(doSomethingAsync));
}
doSomethingWith([1, 2, 3]).then(function(results) {
console.log("Results:", JSON.stringify(results));
});
function doSomethingAsync(value) {
console.log("Starting async operation for " + value);
return new Promise(function(resolve) {
setTimeout(function() {
console.log("Completing async operation for " + value);
resolve(value * 2);
}, Math.floor(Math.random() * 200));
});
}
.as-console-wrapper { max-height: 100% !important; }
Note that Promise.all
resolves its promise with an array of the results of all of the promises you give it when they are all resolved, or rejects its promise when the first of the promises you give it rejects.
Series
Suppose you don’t want the operations to be in parallel? If you want to run them one after another, you need to wait for each operation to complete before you start the next. Here’s an example of a function that does that and calls a callback with the result:
function doSomethingWith(theArray, callback) {
var results = [];
doOne(0);
function doOne(index) {
if (index < theArray.length) {
doSomethingAsync(theArray[index], function(result) {
results.push(result);
doOne(index + 1);
});
} else {
// Done!
callback(results);
}
}
}
doSomethingWith(theArray, function(results) {
console.log("Results:", results);
});
(Since we’re doing the work in series, we can just use results.push(result)
since we know we won’t get results out of order. In the above we could have used results[index] = result;
, but in some of the following examples we don’t have an index to use.)
Example:
function doSomethingWith(theArray, callback) {
var results = [];
doOne(0);
function doOne(index) {
if (index < theArray.length) {
doSomethingAsync(theArray[index], function(result) {
results.push(result);
doOne(index + 1);
});
} else {
// Done!
callback(results);
}
}
}
doSomethingWith([1, 2, 3], function(results) {
console.log("Results:", JSON.stringify(results));
});
function doSomethingAsync(value, callback) {
console.log("Starting async operation for " + value);
setTimeout(function() {
console.log("Completing async operation for " + value);
callback(value * 2);
}, Math.floor(Math.random() * 200));
}
.as-console-wrapper { max-height: 100% !important; }
(Or, again, build a wrapper for doSomethingAsync
that gives you a promise and do the below…)
If doSomethingAsync
gives you a Promise, if you can use ES2017+ syntax (perhaps with a transpiler like Babel), you can use an async
function with for-of
and await
:
async function doSomethingWith(theArray) {
const results = [];
for (const entry of theArray) {
results.push(await doSomethingAsync(entry));
}
return results;
}
doSomethingWith(theArray).then(results => {
console.log("Results:", results);
});
Example:
async function doSomethingWith(theArray) {
const results = [];
for (const entry of theArray) {
results.push(await doSomethingAsync(entry));
}
return results;
}
doSomethingWith([1, 2, 3]).then(function(results) {
console.log("Results:", JSON.stringify(results));
});
function doSomethingAsync(value) {
console.log("Starting async operation for " + value);
return new Promise(function(resolve) {
setTimeout(function() {
console.log("Completing async operation for " + value);
resolve(value * 2);
}, Math.floor(Math.random() * 200));
});
}
.as-console-wrapper { max-height: 100% !important; }
If you can’t use ES2017+ syntax (yet), you can use a variation on the “Promise reduce” pattern (this is more complex than the usual Promise reduce because we’re not passing the result from one into the next, but instead gathering up their results in an array):
function doSomethingWith(theArray) {
return theArray.reduce(function(p, entry) {
return p.then(function(results) {
return doSomethingAsync(entry).then(function(result) {
results.push(result);
return results;
});
});
}, Promise.resolve([]));
}
doSomethingWith(theArray).then(function(results) {
console.log("Results:", results);
});
Example:
function doSomethingWith(theArray) {
return theArray.reduce(function(p, entry) {
return p.then(function(results) {
return doSomethingAsync(entry).then(function(result) {
results.push(result);
return results;
});
});
}, Promise.resolve([]));
}
doSomethingWith([1, 2, 3]).then(function(results) {
console.log("Results:", JSON.stringify(results));
});
function doSomethingAsync(value) {
console.log("Starting async operation for " + value);
return new Promise(function(resolve) {
setTimeout(function() {
console.log("Completing async operation for " + value);
resolve(value * 2);
}, Math.floor(Math.random() * 200));
});
}
.as-console-wrapper { max-height: 100% !important; }
…which is less cumbersome with ES2015+ arrow functions:
function doSomethingWith(theArray) {
return theArray.reduce((p, entry) => p.then(results => doSomethingAsync(entry).then(result => {
results.push(result);
return results;
})), Promise.resolve([]));
}
doSomethingWith(theArray).then(results => {
console.log("Results:", results);
});
Example:
function doSomethingWith(theArray) {
return theArray.reduce((p, entry) => p.then(results => doSomethingAsync(entry).then(result => {
results.push(result);
return results;
})), Promise.resolve([]));
}
doSomethingWith([1, 2, 3]).then(function(results) {
console.log("Results:", JSON.stringify(results));
});
function doSomethingAsync(value) {
console.log("Starting async operation for " + value);
return new Promise(function(resolve) {
setTimeout(function() {
console.log("Completing async operation for " + value);
resolve(value * 2);
}, Math.floor(Math.random() * 200));
});
}
.as-console-wrapper { max-height: 100% !important; }
4
Angular 1
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.
An example found in documentation 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.
Angular 2 and later
In Angular 2 with look at the following example, but its recommended to use observables with Angular 2.
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 specification.
2
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/
ES6 way (async – await)
(function(){
async function getJoke(){
let response = await fetch('http://api.icndb.com/jokes/random');
let data = await response.json();
return data.value;
}
getJoke().then((joke) => {
console.log(joke);
});
})();
0
This is one of the places which two-way data binding or store concept that’s used in many new JavaScript frameworks will work great for you…
So if you are using Angular, React, or any other frameworks which do two-way data binding or store concept, this issue is simply fixed for you, so in easy words, your result is undefined
at the first stage, so you have got result = undefined
before you receive the data, then as soon as you get the result, it will be updated and get assigned to the new value which response of your Ajax call…
But how you can do it in pure JavaScript or jQuery for example as you asked in this question?
You can use a callback, promise and recently observable to handle it for you. For example, in promises we have some function like success()
or then()
which will be executed when your data is ready for you. The same with callback or the subscribe function on an observable.
For example, in your case which you are using jQuery, you can do something like this:
$(document).ready(function(){
function foo() {
$.ajax({url: "api/data", success: function(data){
fooDone(data); // After we have data, we pass it to fooDone
}});
};
function fooDone(data) {
console.log(data); // fooDone has the data and console.log it
};
foo(); // The call happens here
});
For more information, study promises and observables which are newer ways to do this async stuff.
3
It’s a very common issue we face while struggling with the ‘mysteries’ of JavaScript. Let me try demystifying this mystery today.
Let’s start with a simple JavaScript function:
function foo(){
// Do something
return 'wohoo';
}
let bar = foo(); // 'bar' is 'wohoo' here
That’s a simple synchronous function call (where each line of code is ‘finished with its job’ before the next one in sequence), and the result is same as expected.
Now let’s add a bit of twist, by introducing a little delay in our function, so that all lines of code are not ‘finished’ in sequence. Thus, it will emulate the asynchronous behavior of the function:
function foo(){
setTimeout( ()=> {
return 'wohoo';
}, 1000)
}
let bar = foo() // 'bar' is undefined here
So there you go; that delay just broke the functionality we expected! But what exactly happened? Well, it’s actually pretty logical if you look at the code.
The function foo()
, upon execution, returns nothing (thus returned value is undefined
), but it does start a timer, which executes a function after 1 second to return ‘wohoo’. But as you can see, the value that’s assigned to bar is the immediately returned stuff from foo(), which is nothing, i.e., just undefined
.
So, how do we tackle this issue?
Let’s ask our function for a promise.
Promise is really about what it means: it means that the function guarantees you to provide with any output it gets in future. So let’s see it in action for our little problem above:
function foo(){
return new Promise((resolve, reject) => { // I want foo() to PROMISE me something
setTimeout ( function(){
// Promise is RESOLVED, when the execution reaches this line of code
resolve('wohoo') // After 1 second, RESOLVE the promise with value 'wohoo'
}, 1000 )
})
}
let bar;
foo().then( res => {
bar = res;
console.log(bar) // Will print 'wohoo'
});
Thus, the summary is – to tackle the asynchronous functions like Ajax-based calls, etc., you can use a promise to resolve
the value (which you intend to return). Thus, in short you resolve value instead of returning, in asynchronous functions.
UPDATE (Promises with async/await)
Apart from using then/catch
to work with promises, there exists one more approach. The idea is to recognize an asynchronous function and then wait for the promises to resolve, before moving to the next line of code. It’s still just the promises
under the hood, but with a different syntactical approach. To make things clearer, you can find a comparison below:
then/catch version:
function saveUsers(){
getUsers()
.then(users => {
saveSomewhere(users);
})
.catch(err => {
console.error(err);
})
}
async/await version:
async function saveUsers(){
try{
let users = await getUsers()
saveSomewhere(users);
}
catch(err){
console.error(err);
}
}
2
Another approach to return a value from an asynchronous function, is to pass in an object that will store the result from the asynchronous function.
Here is an example of the same:
var async = require("async");
// This wires up result back to the caller
var result = {};
var asyncTasks = [];
asyncTasks.push(function(_callback){
// some asynchronous operation
$.ajax({
url: '...',
success: function(response) {
result.response = response;
_callback();
}
});
});
async.parallel(asyncTasks, function(){
// result is available after performing asynchronous operation
console.log(result)
console.log('Done');
});
I am using the result
object to store the value during the asynchronous operation. This allows the result be available even after the asynchronous job.
I use this approach a lot. I would be interested to know how well this approach works where wiring the result back through consecutive modules is involved.
1
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.
3
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.
The short answer is, you have to implement a callback like this:
function callback(response) {
// Here you can do what ever you want with the response object.
console.log(response);
}
$.ajax({
url: "...",
success: callback
});
0
JavaScript is single threaded.
The browser can be divided into three parts:
-
Event Loop
-
Web API
-
Event Queue
The event loop runs for forever, i.e., kind of an infinite loop. The event queue is where all your functions are pushed on some event (example: click).
This is one by one carried out of queue and put into the event loop which executes this function and prepares itself for the next one after the first one is executed. This means execution of one function doesn’t start until the function before it in the queue is executed in the event loop.
Now let us think we pushed two functions in a queue. One is for getting a data from the server and another utilises that data. We pushed the serverRequest() function in the queue first and then the utiliseData() function. The serverRequest function goes in the event loop and makes a call to server as we never know how much time it will take to get data from server, so this process is expected to take time and so we busy our event loop thus hanging our page.
That’s where Web API come into the role. It takes this function from the event loop and deals with the server making the event loop free, so that we can execute the next function from the queue.
The next function in the queue is utiliseData() which goes in the loop, but because of no data available, it goes to waste and execution of the next function continues until the end of the queue. (This is called Async calling, i.e., we can do something else until we get data.)
Let us suppose our serverRequest() function had a return statement in code. When we get back data from the server Web API, it will push it in the queue at the end of queue.
As it gets pushed at the end of the queue, we cannot utilise its data as there isn’t any function left in our queue to utilise this data. Thus it is not possible to return something from the async call.
Thus the solution to this is callback or promise.
- An image from one of the answers here correctly explains callback use…*
We give our function (function utilising data returned from the server) to a function calling the server.
function doAjax(callbackFunc, method, url) {
var xmlHttpReq = new XMLHttpRequest();
xmlHttpReq.open(method, url);
xmlHttpReq.onreadystatechange = function() {
if (xmlHttpReq.readyState == 4 && xmlHttpReq.status == 200) {
callbackFunc(xmlHttpReq.responseText);
}
}
xmlHttpReq.send(null);
}
In my code it is called as:
function loadMyJson(categoryValue){
if(categoryValue === "veg")
doAjax(print, "GET", "http://localhost:3004/vegetables");
else if(categoryValue === "fruits")
doAjax(print, "GET", "http://localhost:3004/fruits");
else
console.log("Data not found");
}
JavaScript.info callback
0
2017 answer: you can now do exactly what you want in every current browser and Node.js
This is quite simple:
- Return a Promise
- Use the ‘await’, which will tell JavaScript to await the promise to be resolved into a value (like the HTTP response)
- Add the ‘async’ 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.js 8
4
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)
});
Another solution is to execute code via the sequential executor nsynjs.
If the underlying function is promisified
nsynjs will evaluate all promises sequentially, and put the promise result into the data
property:
function synchronousCode() {
var getURL = function(url) {
return window.fetch(url).data.text().data;
};
var url = 'https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js';
console.log('received bytes:',getURL(url).length);
};
nsynjs.run(synchronousCode,{},function(){
console.log('synchronousCode done');
});
<script src="https://rawgit.com/amaksr/nsynjs/master/nsynjs.js"></script>
If the underlying function is not promisified
Step 1. Wrap the function with a callback into the nsynjs-aware wrapper (if it has a promisified version, you can skip this step):
var ajaxGet = function (ctx,url) {
var res = {};
var ex;
$.ajax(url)
.done(function (data) {
res.data = data;
})
.fail(function(e) {
ex = e;
})
.always(function() {
ctx.resume(ex);
});
return res;
};
ajaxGet.nsynjsHasCallback = true;
Step 2. Put synchronous logic into function:
function process() {
console.log('got data:', ajaxGet(nsynjsCtx, "data/file1.json").data);
}
Step 3. Run function in synchronous manner via nsynjs:
nsynjs.run(process,this,function () {
console.log("synchronous function finished");
});
Nsynjs will evaluate all operators and expressions step-by-step, pausing execution in case if the result of some slow function is not ready.
More examples are here.
1
1. A first stumbling step
As for many others, my encounter with asynchronous calls was puzzling at first.
I don’t remember the details, but I may have tried something like:
let result;
$.ajax({
url: 'https://jsonplaceholder.typicode.com/todos/1',
success: function (response) {
console.log('nInside $.ajax:');
console.log(response);
result = response;
},
});
console.log('Finally, the result: ' + result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
Whoops!
The output of the line
console.log('Finally, the result: ' + result);
which I thought would be printed last, is printed before the other output!
– And it doesn’t contain the result: it just prints undefined
. 1
How come?
A helpful insight
I distinctly remember my first aha (💡) moment about asynchronous calls.
It was :
you actually don’t want to get the data out of a callback; you want to get your data-needing action into the callback!
2
This is true in the example above.
2. Plain JavaScript and a callback function
Luckily, it is possible to write code after the asynchronous call that deals with the response once it has completed.
One alternative is the use of a callback function in a continuation-passing style :
3
const url = 'https://jsonplaceholder.typicode.com/todos/2';
function asynchronousFunc(callback) {
const request = new XMLHttpRequest();
request.open('GET', url);
request.send();
request.onload = function () {
if (request.readyState === request.DONE) {
console.log('The request is done. Now calling back.');
callback(request.responseText);
}
};
}
asynchronousFunc(function (result) {
console.log('This is the start of the callback function. Result:');
console.log(result);
console.log('The callback function finishes on this line. THE END!');
});
console.log('LAST in the code, but executed FIRST!');
.as-console-wrapper { max-height: 100% !important; top: 0; }
Note how the function asynchronousFunc
is void
. It returns nothing.
asynchronousFunc
is called with an anonymous callback function,
(asynchronousFunc(function (result) {...});
).
This executes the desired actions on the result after the request has completed – when the responseText
is available.
Running the above snippet shows how I will probably not want to write any code after the asynchronous call (such as the line
LAST in the code, but executed FIRST!
).
Why? – Because such code will run before the asynchronous call delivers any response data.
Doing so is bound to cause confusion when comparing the code with the output.
3. Promise with .then()
The .then()
construct was introduced in the ECMA-262 6th Edition in June 2015.
The code below is plain JavaScript, replacing the old-school XMLHttpRequest with Fetch. 4
fetch('https://api.chucknorris.io/jokes/random')
.then((response) => response.json())
.then((responseBody) => {
console.log('Using .then() :');
console.log(responseBody.value + 'n');
});
.as-console-wrapper { max-height: 100% !important; top: 0; }
4. Promise with async
/await
The async
/await
construct was introduced in the ECMA-262 8th Edition in June 2017.
async function awaitAndReceivePromise() {
const responseBody = (
await fetch('https://api.quotable.io/quotes/random')
).json();
console.log('Using async/await:');
const obj = (await responseBody)[0];
console.log('"' + obj.content + '" – ' + obj.author + 'n');
}
awaitAndReceivePromise();
.as-console-wrapper { max-height: 100% !important; top: 0; }
A word of warning is warranted if you decide to go with the async
/await
construct.
Note in the above snippet how await
is needed in two places.
If forgotten in the first place, there will be no output at all.
If forgotten in the second place, the only output will be Using async/await:
– nothing else gets printed.
Forgetting the async
prefix of the function is maybe the worst of all – you’ll get a "SyntaxError"
– and likely no hint about the missing async
keyword.
All the above examples succinctly convey how asynchronous calls may be used on toyish APIs. 5
References
- Some questions and answers about asynchronous calls
- Using plain JavaScript and a callback function
- Continuation-passing style
- XMLHttpRequest at mdn web docs
- XMLHttpRequest: onload vs. onreadystatechange
- XMLHttpRequest.responseText
- An example demonstrating
async
/await
- Using the Fetch API
- Using promises
- The XMLHttpRequest Standard
- The Fetch Standard
- The Web Hypertext Application Technology Working Group (WHATWG)
- Links to ECMA specifications
- How can I fetch an array of URLs with Promise.all?
1
Expressed by the asker of the question as they all return undefined
.
2
Here is more on how asynchronous calls may be confusing at first.
3
Like the X in AJAX, the name XMLHttpRequest
is misleading – it can be used to retrieve any type of data, not just XML.
These days, the data format of Web APIs is ubiquitously JSON, not XML.
4
Fetch returns a Promise.
I was surprised to learn that neither XMLHttpRequest nor Fetch are part of the ECMAScript standard.
The reason JavaScript can access them here is that the web browser provides them.
The Fetch Standard and the XMLHttpRequest Standard are both upheld by the Web Hypertext Application Technology Working Group which was formed in June 2004.
5
You might also be interested in
How can I fetch an array of URLs with Promise.all?.
ECMAScript 6 has ‘generators’ which allow you to easily program in an asynchronous style.
function* myGenerator() {
const callback = yield;
let [response] = yield $.ajax("https://stackoverflow.com", {complete: callback});
console.log("response is:", response);
// examples of other things you can do
yield setTimeout(callback, 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(); // Create generator
gen.next(); // Start it
gen.next((...args) => gen.next([...args])); // Set its callback function
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.
The callback ...args
are wrapped in an array and destructured when you read them so that the pattern can cope with callbacks that have multiple arguments. For example with node fs:
const [err, data] = yield fs.readFile(filePath, "utf-8", callback);
1
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 asynchronicity is waiting for some remote system to respond 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 JavaScript 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. JavaScript, and other declarative languages execute one statement after another without waiting.
The classic JavaScript approach to this problem, taking advantage of the fact that JavaScript supports functions as first-class objects which can be passed around, is to pass a function as a parameter to the asynchronous request, which it will then invoke when it has completed 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 not 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 for 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, “JavaScript 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 JavaScript 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 ECMAScript (ES), but it 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);
}
Here are some approaches to work with asynchronous requests:
- Browser Promise object
- Q – A promise library for JavaScript
- A+ Promises.js
- jQuery deferred
- XMLHttpRequest API
- 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();
1
Short answer: Your foo()
method returns immediately, while the $ajax()
call executes asynchronously after the function returns. The problem is then how or where to store the results retrieved by the async call once it returns.
Several solutions have been given in this thread. Perhaps the easiest way is to pass an object to the foo()
method, and to store the results in a member of that object after the async call completes.
function foo(result) {
$.ajax({
url: '...',
success: function(response) {
result.response = response; // Store the async result
}
});
}
var result = { response: null }; // Object to hold the async result
foo(result); // Returns before the async completes
Note that the call to foo()
will still return nothing useful. However, the result of the async call will now be stored in result.response
.
1
Use a callback()
function inside the foo()
success.
Try it 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();
Using Promise
The most perfect answer to this question is using Promise
.
function ajax(method, url, params) {
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.onload = function() {
resolve(this.responseText);
};
xhr.onerror = reject;
xhr.open(method, url);
xhr.send(params);
});
}
Usage
ajax("GET", "/test", "acrive=1").then(function(result) {
// Code depending on result
})
.catch(function() {
// An error occurred
});
But wait…!
There is a problem with using promises!
Why should we use our own custom Promise?
I was using this solution for a while until I figured out there is an error in old browsers:
Uncaught ReferenceError: Promise is not defined
So I decided to implement my own Promise class for ES3 to below JavaScript compilers if it’s not defined. Just add this code before your main code and then safely use Promise!
if(typeof Promise === "undefined"){
function _typeof(obj) { "@babel/helpers - typeof"; return
_typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
var Promise = /*#__PURE__*/function () {
"use strict";
function Promise(main) {
_classCallCheck(this, Promise);
this.main = main;
this.mainExecuted = false;
this.resolved = false;
this.rejected = false;
this.promiseChain = [];
this.handleError = function () {};
this.onResolve = this.onResolve.bind(this);
this.onReject = this.onReject.bind(this);
}
_createClass(Promise, [{
key: "then",
value: function then(handleSuccess) {
if (this.resolved) {
if (!this.rejected) {
this.args = handleSuccess(this.args);
}
} else {
this.promiseChain.push(handleSuccess);
this.main(this.onResolve, this.onReject);
this.thenExecuted = true;
}
return this;
}
}, {
key: "catch",
value: function _catch(handleError) {
this.handleError = handleError;
if (!this.mainExecuted) {
this.main(this.onResolve, this.onReject);
this.thenExecuted = true;
}
return this;
}
}, {
key: "onResolve",
value: function onResolve() {
var _this = this;
this.resolved = true;
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
this.args = args;
try {
this.promiseChain.forEach(function (nextFunction) {
_this.args = nextFunction.apply(void 0, _toConsumableArray(_this.args));
});
} catch (error) {
this.promiseChain = [];
this.onReject(error);
}
}
}, {
key: "onReject",
value: function onReject(error) {
this.rejected = true;
this.handleError(error);
}
}]);
return Promise;
}();
}
2
Using ES2017 you should have this as the function declaration.
async function foo() {
var response = await $.ajax({url: '...'})
return response;
}
And executing it like this.
(async function() {
try {
var result = await foo()
console.log(result)
} catch (e) {}
})()
Or the Promise syntax.
foo().then(response => {
console.log(response)
}).catch(error => {
console.log(error)
})
Stack Snippet that demonstrates the code above.
// The function declaration:
async function foo() {
var response = await $.ajax({
url: 'https://jsonplaceholder.typicode.com/todos/1'
})
return response;
}
// Execute it like this:
(async function() {
try {
var result = await foo()
console.log(result)
} catch (e) {}
})()
// Or use Promise syntax:
foo().then(response => {
console.log(response)
}).catch(error => {
console.log(error)
})
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src=
"https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
5
Of course there are many approaches like synchronous request, promise, but from my experience I think you should use the callback approach. It’s natural to asynchronous behavior of JavaScript.
So, your code snippet can be rewritten to be a little different:
function foo() {
var result;
$.ajax({
url: '...',
success: function(response) {
myCallback(response);
}
});
return result;
}
function myCallback(response) {
// Does something.
}
2
The question was:
How do I return the response from an asynchronous call?
which can be interpreted as:
How to make asynchronous code look synchronous?
The solution will be to avoid callbacks, and use a combination of Promises and async/await.
I would like to give an example for an Ajax request.
(Although it can be written in JavaScript, I prefer to write it in Python, and compile it to JavaScript using Transcrypt. It will be clear enough.)
Let’s first enable jQuery usage, to have $
available as S
:
__pragma__ ('alias', 'S', '$')
Define a function which returns a Promise, in this case an Ajax call:
def read(url: str):
deferred = S.Deferred()
S.ajax({'type': "POST", 'url': url, 'data': { },
'success': lambda d: deferred.resolve(d),
'error': lambda e: deferred.reject(e)
})
return deferred.promise()
Use the asynchronous code as if it were synchronous:
async def readALot():
try:
result1 = await read("url_1")
result2 = await read("url_2")
except Exception:
console.warn("Reading a lot failed")
1
Sinh nhật phong cách metal
Tổ chức sinh nhật tại nhà jazz
Dịch vụ sinh nhật xuất sắc hơn
Tiệc sinh nhật cho nhà ngôn ngữ học
Thuê nhóm nhảy metal sinh nhật
Sinh nhật chủ đề sang trọng
Tổ chức sinh nhật tại nhà pop
Dịch vụ sinh nhật hoàn mỹ hơn
Tiệc sinh nhật cho nhà văn học
Trang trí sinh nhật bằng đồ garnet
Sinh nhật phong cách punk rock
Tổ chức sinh nhật tại nhà reggae
Dịch vụ sinh nhật tuyệt vời hơn nữa
Tiệc sinh nhật cho nhà khảo cổ học
Thuê nhóm nhảy punk sinh nhật
Sinh nhật chủ đề ấm cúng
Tổ chức sinh nhật tại nhà blues
Dịch vụ sinh nhật đỉnh cao hơn nữa
Tiệc sinh nhật cho nhà địa lý học
Trang trí sinh nhật bằng đồ aquamarine
Sinh nhật phong cách alternative
Tổ chức sinh nhật tại nhà country
Dịch vụ sinh nhật chất lượng hơn
Tiệc sinh nhật cho nhà thiên văn học
Thuê nhóm nhảy alternative sinh nhật
Sinh nhật chủ đề lãng mạn
Tổ chức sinh nhật tại nhà folk
Dịch vụ sinh nhật sáng chói hơn nữa
Tiệc sinh nhật cho nhà triết học
Trang trí sinh nhật bằng đồ citrine
Sinh nhật phong cách ska
Tổ chức sinh nhật tại nhà techno
Dịch vụ sinh nhật rực rỡ hơn nữa
Tiệc sinh nhật cho nhà kinh tế học
Thuê nhóm nhảy ska sinh nhật
Sinh nhật chủ đề truyền thống
Tổ chức sinh nhật tại nhà electronic
Dịch vụ sinh nhật lung linh hơn nữa
Tiệc sinh nhật cho nhà sử học
Trang trí sinh nhật bằng đồ peridot
Sinh nhật phong cách R&B
Tổ chức sinh nhật tại nhà disco
Dịch vụ sinh nhật đẹp hơn nữa
Tiệc sinh nhật cho nhà nhân học
Thuê nhóm nhảy R&B sinh nhật
Sinh nhật chủ đề phá cách
Tổ chức sinh nhật tại nhà funk
Dịch vụ sinh nhật hoàn toàn hơn nữa
Tiệc sinh nhật cho nhà luật học
Trang trí sinh nhật bằng đồ moonstone
Sinh nhật phong cách gospel
Tổ chức sinh nhật tại nhà soul
Dịch vụ sinh nhật độc đáo hơn nữa
Tiệc sinh nhật cho nhà chính trị học
Thuê nhóm nhảy gospel sinh nhật
Sinh nhật chủ đề nghệ thuật
Tổ chức sinh nhật tại nhà rap
Dịch vụ sinh nhật xuất sắc hơn nữa
Tiệc sinh nhật cho nhà tài chính học
Trang trí sinh nhật bằng đồ onyx
Sinh nhật phong cách hip hop dance
Tổ chức sinh nhật tại nhà indie
Dịch vụ sinh nhật hoàn mỹ hơn nữa
Tiệc sinh nhật cho nhà quản trị học
Thuê nhóm nhảy hip hop sinh nhật
Sinh nhật chủ đề đồng quê
Tổ chức sinh nhật tại nhà acoustic
Dịch vụ sinh nhật tuyệt vời hơn hết
Tiệc sinh nhật cho nhà kỹ thuật học
Trang trí sinh nhật bằng đồ jade
Sinh nhật phong cách breakdance
Tổ chức sinh nhật tại nhà chill
Dịch vụ sinh nhật đỉnh cao hơn hết
Tiệc sinh nhật cho nhà công nghệ học
Thuê nhóm nhảy breakdance sinh nhật
Sinh nhật chủ đề hoàng gia
Tổ chức sinh nhật tại nhà lounge
Dịch vụ sinh nhật chất lượng hơn hết
Tiệc sinh nhật cho nhà nghiên cứu học
Trang trí sinh nhật bằng đồ lapis lazuli
Sinh nhật phong cách street dance
Tổ chức sinh nhật tại nhà classical
Dịch vụ sinh nhật sáng chói hơn hết
Tiệc sinh nhật cho nhà phân tích học
Thuê nhóm nhảy street sinh nhật
Sinh nhật chủ đề huyền bí
Tổ chức sinh nhật tại nhà grunge
Dịch vụ sinh nhật rực rỡ hơn hết
Tiệc sinh nhật cho nhà xã hội học
Trang trí sinh nhật bằng đồ malachite
Sinh nhật phong cách contemporary
Tổ chức sinh nhật tại nhà metal
Dịch vụ sinh nhật lung linh hơn hết
Tiệc sinh nhật cho nhà giáo dục học
Thuê nhóm nhảy contemporary sinh nhật
Sinh nhật chủ đề nhiệt đới
Tổ chức sinh nhật tại nhà punk rock
Dịch vụ sinh nhật đẹp hơn hết
Tiệc sinh nhật cho nhà tâm lý học
Trang trí sinh nhật bằng đồ amber
Sinh nhật phong cách ballet
Tổ chức sinh nhật tại nhà alternative
Dịch vụ sinh nhật hoàn toàn hơn hết
Tiệc sinh nhật cho nhà ngôn ngữ học
Thuê nhóm nhảy ballet sinh nhật
Sinh nhật chủ đề mùa đông
Tổ chức sinh nhật tại nhà ska
Dịch vụ sinh nhật độc đáo hơn hết
Trợ lý AI thông minh nhất cho bạn
Khám phá công nghệ AI tại đây
Trải nghiệm trợ lý ảo tuyệt vời
Công cụ AI hỗ trợ mọi công việc
Tăng năng suất với AI thông minh
AI thay đổi cách bạn làm việc
Trợ lý ảo đáng tin cậy nhất
Khám phá tương lai với AI
Công nghệ AI tiên tiến cho bạn
Hỗ trợ thông minh từ trợ lý AI
AI giúp bạn tiết kiệm thời gian
Trợ lý ảo tốt nhất hiện nay
Công nghệ AI đỉnh cao
Khám phá sức mạnh của AI
Trợ lý AI hỗ trợ 24/7
Công cụ AI cho mọi nhu cầu
AI thông minh, nhanh chóng
Trợ lý ảo dẫn đầu xu hướng
Công nghệ AI dành cho bạn
Hỗ trợ công việc với AI
Trợ lý AI tối ưu hóa công việc
Khám phá AI hiện đại
Công cụ AI đáng kinh ngạc
Trợ lý ảo thông minh vượt trội
AI giúp bạn thành công
Công nghệ AI đáng tin cậy
Trợ lý ảo cho tương lai
Khám phá công cụ AI mới
AI hỗ trợ mọi lúc mọi nơi
Trợ lý ảo thông minh hàng đầu
Công nghệ AI thay đổi cuộc sống
Hỗ trợ tối đa với AI
Trợ lý AI sáng tạo nhất
Công cụ AI mạnh mẽ
Khám phá trợ lý ảo AI
AI thông minh cho mọi người
Trợ lý ảo tối ưu nhất
Công nghệ AI vượt trội
Hỗ trợ công việc bằng AI
Trợ lý AI cho mọi ngành
Khám phá sức mạnh AI
Công cụ AI thông minh nhất
Trợ lý ảo dẫn dắt tương lai
AI hỗ trợ không giới hạn
Công nghệ AI sáng tạo
Trợ lý ảo hiệu quả nhất
Khám phá công nghệ AI đỉnh cao
AI giúp bạn tỏa sáng
Trợ lý ảo thông minh toàn diện
Công cụ AI thay đổi mọi thứ
Trợ lý AI giúp bạn làm việc nhanh hơn
Công nghệ AI hiện đại nhất hiện nay
Trải nghiệm AI thông minh vượt bậc
Công cụ AI hỗ trợ sáng tạo
Trợ lý ảo dành cho mọi nhà
AI tối ưu hóa công việc hàng ngày
Khám phá trợ lý AI tiên tiến
Công nghệ AI cho doanh nghiệp
Trợ lý ảo giúp bạn tiết kiệm sức lực
AI thông minh hỗ trợ cá nhân
Công cụ AI cho tương lai gần
Trợ lý ảo tối ưu mọi tác vụ
Khám phá công nghệ AI độc đáo
AI giúp bạn đạt hiệu quả cao
Trợ lý ảo thông minh và thân thiện
Công nghệ AI dành cho mọi ngành
Trợ lý AI hỗ trợ liên tục
Khám phá sức mạnh AI vượt trội
Công cụ AI giúp bạn nổi bật
Trợ lý ảo cho công việc hiện đại
AI thông minh dẫn đầu thời đại
Công nghệ AI hỗ trợ toàn diện
Trợ lý ảo giúp bạn sáng tạo hơn
Khám phá AI thông minh hàng đầu
Công cụ AI tối ưu cho bạn
Trợ lý AI thay đổi cách làm việc
Công nghệ AI mạnh mẽ và linh hoạt
Trợ lý ảo thông minh cho mọi người
AI hỗ trợ công việc hiệu quả
Khám phá công cụ AI sáng tạo
Trợ lý ảo giúp bạn thành công hơn
Công nghệ AI dẫn dắt tương lai
Trợ lý AI tối ưu cho doanh nghiệp
AI thông minh hỗ trợ mọi lúc
Công cụ AI dành cho sáng tạo
Trợ lý ảo giúp bạn tiết kiệm chi phí
Khám phá trợ lý AI độc quyền
Công nghệ AI thay đổi mọi ngành
Trợ lý AI thông minh cho cuộc sống
AI hỗ trợ công việc nhóm
Công cụ AI hiện đại và mạnh mẽ
Trợ lý ảo tối ưu hóa thời gian
Khám phá sức mạnh AI thông minh
Công nghệ AI cho mọi nhu cầu
Trợ lý AI giúp bạn đi trước thời đại
AI thông minh hỗ trợ cá nhân hóa
Công cụ AI tối ưu cho công việc
Trợ lý ảo dẫn đầu công nghệ
Khám phá AI vượt xa mong đợi
Công nghệ AI sáng tạo không ngừng
Trợ lý AI thông minh và hiệu quả
AI hỗ trợ bạn mọi lúc mọi nơi
Công cụ AI thay đổi cách sống
Trợ lý ảo tối ưu cho tương lai
Khám phá công nghệ AI tiên phong
Công nghệ AI giúp bạn tỏa sáng
Trợ lý AI hỗ trợ công việc sáng tạo
AI thông minh cho mọi lĩnh vực
Công cụ AI dẫn đầu xu hướng
Trợ lý ảo giúp bạn phát triển
Khám phá trợ lý AI mạnh mẽ
Công nghệ AI hỗ trợ không ngừng
Trợ lý AI thông minh cho doanh nhân
AI tối ưu hóa mọi quy trình
Công cụ AI sáng tạo và thông minh
Trợ lý ảo giúp bạn quản lý thời gian
Khám phá sức mạnh của công nghệ AI
Công nghệ AI thay đổi cách nghĩ
Trợ lý AI hỗ trợ mọi dự án
AI thông minh cho cuộc sống hiện đại
Công cụ AI giúp bạn đi xa hơn
Trợ lý ảo tối ưu cho sáng tạo
Khám phá AI thông minh vượt trội
Công nghệ AI dành cho tương lai
Trợ lý AI giúp bạn thành công lớn
AI hỗ trợ công việc hiệu quả hơn
Công cụ AI thông minh và linh hoạt
Trợ lý ảo dẫn đầu mọi xu hướng
Khám phá công nghệ AI sáng tạo
Công nghệ AI tối ưu hóa cuộc sống
Trợ lý AI thông minh cho mọi ngành
AI giúp bạn quản lý công việc
Công cụ AI dành cho thành công
Trợ lý ảo hỗ trợ không giới hạn
Khám phá trợ lý AI thông minh nhất
Công nghệ AI thay đổi mọi thứ
Trợ lý AI tối ưu cho doanh nghiệp
AI thông minh hỗ trợ sáng tạo
Công cụ AI giúp bạn tiết kiệm thời gian
Trợ lý ảo dẫn dắt tương lai
Khám phá sức mạnh AI hiện đại
Công nghệ AI cho mọi người
Trợ lý AI thông minh và đáng tin cậy
AI hỗ trợ bạn vượt qua thử thách
Công cụ AI tối ưu hóa công việc
Trợ lý ảo giúp bạn phát triển nhanh
Khám phá công nghệ AI tiên tiến
Công nghệ AI sáng tạo cho bạn
Trợ lý AI hỗ trợ mọi nhu cầu
AI thông minh thay đổi cuộc chơi
Công cụ AI dẫn đầu mọi lĩnh vực
Trợ lý ảo tối ưu cho mọi tác vụ
Khám phá trợ lý AI vượt trội
Công nghệ AI giúp bạn thành công
Trợ lý AI thông minh cho tương lai
AI hỗ trợ công việc sáng tạo
Công cụ AI thông minh vượt bậc
Trợ lý ảo giúp bạn quản lý hiệu quả
Khám phá sức mạnh AI sáng tạo
Công nghệ AI tối ưu cho cuộc sống
Trợ lý AI thông minh và hiện đại
AI giúp bạn đi trước xu hướng
Công cụ AI hỗ trợ không ngừng
Trợ lý ảo dẫn đầu công nghệ AI
Khám phá công nghệ AI thông minh
Công nghệ AI thay đổi cách làm việc
Trợ lý AI tối ưu hóa sáng tạo
AI thông minh cho mọi công việc
Công cụ AI giúp bạn phát triển
Trợ lý ảo hỗ trợ mọi lúc
Khám phá trợ lý AI tiên phong
Công nghệ AI sáng tạo vượt trội
Trợ lý AI thông minh cho doanh nghiệp
AI giúp bạn tối ưu hóa thời gian
Công cụ AI mạnh mẽ cho bạn
Trợ lý ảo dẫn dắt mọi ngành
Khám phá sức mạnh AI thông minh
Công nghệ AI hỗ trợ toàn diện
Trợ lý AI tối ưu cho sáng tạo
AI thông minh thay đổi tương lai
Công cụ AI giúp bạn thành công hơn
Trợ lý ảo hỗ trợ không giới hạn
Khám phá công nghệ AI hiện đại
Công nghệ AI sáng tạo cho mọi người
Trợ lý AI thông minh vượt mong đợi
AI giúp bạn quản lý công việc tốt hơn
Công cụ AI tối ưu cho doanh nghiệp
Trợ lý ảo dẫn đầu xu hướng công nghệ
Khám phá trợ lý AI sáng tạo
Công nghệ AI hỗ trợ mọi lĩnh vực
Trợ lý AI thông minh cho cuộc sống
AI tối ưu hóa công việc hàng ngày
Công cụ AI giúp bạn đi xa hơn
Trợ lý ảo hỗ trợ mọi nhu cầu
Khám phá sức mạnh AI vượt trội
Công nghệ AI thay đổi cách sống
Trợ lý AI tối ưu cho tương lai
AI thông minh hỗ trợ sáng tạo
Công cụ AI dẫn đầu mọi xu hướng
Trợ lý ảo giúp bạn phát triển nhanh
Khám phá công nghệ AI tiên phong
Công nghệ AI sáng tạo không ngừng
Trợ lý AI thông minh cho doanh nhân
AI giúp bạn tối ưu hóa hiệu suất
Công cụ AI mạnh mẽ và hiệu quả
Trợ lý ảo dẫn dắt tương lai
Khám phá trợ lý AI thông minh
Công nghệ AI hỗ trợ không giới hạn
Trợ lý AI tối ưu cho mọi ngành
AI thông minh thay đổi mọi thứ
Công cụ AI giúp bạn thành công lớn
Trợ lý ảo hỗ trợ mọi lúc mọi nơi
Khám phá sức mạnh AI hiện đại
Công nghệ AI sáng tạo cho bạn
Trợ lý AI thông minh vượt trội
AI giúp bạn quản lý thời gian tốt hơn
Công cụ AI tối ưu hóa sáng tạo
Trợ lý ảo dẫn đầu công nghệ
Khám phá công nghệ AI vượt bậc
Công nghệ AI hỗ trợ mọi công việc
Trợ lý AI thông minh cho mọi người
AI tối ưu hóa cuộc sống hàng ngày
Công cụ AI giúp bạn phát triển
Trợ lý ảo hỗ trợ không ngừng
Khám phá trợ lý AI tiên tiến
Công nghệ AI sáng tạo và mạnh mẽ
Trợ lý AI thông minh cho doanh nghiệp
AI giúp bạn đi trước thời đại
Công cụ AI tối ưu cho mọi nhu cầu
Trợ lý ảo dẫn dắt mọi xu hướng
Khám phá sức mạnh AI sáng tạo
Công nghệ AI thay đổi cách làm việc
Trợ lý AI thông minh và hiệu quả
AI hỗ trợ bạn vượt qua khó khăn
Công cụ AI giúp bạn tỏa sáng
Trợ lý ảo tối ưu cho công việc
Khám phá công nghệ AI thông minh
Công nghệ AI sáng tạo không giới hạn
Trợ lý AI thông minh cho tương lai
AI giúp bạn quản lý hiệu quả hơn
Công cụ AI tối ưu hóa sáng tạo
Trợ lý ảo dẫn đầu mọi lĩnh vực
Khám phá trợ lý AI vượt trội
Công nghệ AI hỗ trợ toàn diện
Trợ lý AI thông minh cho mọi ngành
AI tối ưu hóa công việc sáng tạo
Công cụ AI giúp bạn thành công
Trợ lý ảo hỗ trợ mọi lúc
Khám phá sức mạnh AI tiên phong
Công nghệ AI sáng tạo vượt bậc
Trợ lý AI thông minh và mạnh mẽ
AI giúp bạn tối ưu hóa thời gian
Công cụ AI dẫn đầu công nghệ
Trợ lý ảo tối ưu cho doanh nghiệp
Khám phá công nghệ AI hiện đại
Công nghệ AI hỗ trợ không ngừng
Trợ lý AI thông minh cho cuộc sống
AI tối ưu hóa mọi công việc
Công cụ AI giúp bạn phát triển nhanh
Trợ lý ảo dẫn dắt tương lai
Khám phá trợ lý AI sáng tạo
Công nghệ AI thay đổi mọi ngành
Trợ lý AI thông minh vượt trội
AI giúp bạn quản lý công việc
Công cụ AI tối ưu cho sáng tạo
Trợ lý ảo hỗ trợ mọi nhu cầu
Khám phá sức mạnh AI thông minh
Công nghệ AI sáng tạo cho bạn
Trợ lý AI thông minh và hiệu quả
AI tối ưu hóa cuộc sống hiện đại
Công cụ AI giúp bạn đi trước xu hướng
Trợ lý ảo dẫn đầu mọi xu hướng
Khám phá công nghệ AI tiên tiến
Công nghệ AI hỗ trợ toàn diện
Trợ lý AI thông minh cho doanh nghiệp
AI giúp bạn tối ưu hóa công việc
Công cụ AI sáng tạo và mạnh mẽ
Trợ lý ảo tối ưu cho tương lai
Khám phá trợ lý AI vượt bậc
Công nghệ AI thay đổi cách sống
Trợ lý AI thông minh cho mọi người
AI hỗ trợ bạn thành công lớn
Công cụ AI giúp bạn phát triển
Trợ lý ảo dẫn dắt mọi lĩnh vực
Khám phá sức mạnh AI sáng tạo
Công nghệ AI tối ưu cho bạn
Trợ lý AI thông minh và đáng tin cậy
AI giúp bạn quản lý thời gian
Công cụ AI hỗ trợ không giới hạn
Trợ lý ảo tối ưu hóa sáng tạo
Khám phá công nghệ AI thông minh
Công nghệ AI sáng tạo vượt trội
Trợ lý AI thông minh cho doanh nhân
AI tối ưu hóa mọi quy trình
Công cụ AI giúp bạn tỏa sáng
Trợ lý ảo dẫn đầu công nghệ
Khám phá trợ lý AI tiên phong
Công nghệ AI hỗ trợ mọi lúc
Trợ lý AI thông minh vượt mong đợi
AI giúp bạn đi trước thời đại
Công cụ AI tối ưu cho công việc
Trợ lý ảo hỗ trợ không ngừng
Khám phá sức mạnh AI hiện đại
Công nghệ AI sáng tạo cho mọi ngành
Trợ lý AI thông minh và hiệu quả
AI tối ưu hóa cuộc sống hàng ngày
Công cụ AI giúp bạn phát triển nhanh
Trợ lý ảo dẫn dắt tương lai
Khám phá công nghệ AI vượt trội
Công nghệ AI hỗ trợ toàn diện
Trợ lý AI thông minh cho mọi người
AI giúp bạn quản lý công việc
Công cụ AI tối ưu hóa sáng tạo
Trợ lý ảo hỗ trợ mọi nhu cầu
Khám phá trợ lý AI sáng tạo
Công nghệ AI thay đổi mọi thứ
Trợ lý AI thông minh vượt bậc
AI tối ưu hóa công việc hiệu quả
Công cụ AI giúp bạn thành công
Trợ lý ảo dẫn đầu mọi xu hướng
Khám phá sức mạnh AI thông minh
Công nghệ AI sáng tạo không ngừng
Trợ lý AI thông minh cho doanh nghiệp
AI giúp bạn tối ưu hóa thời gian
Công cụ AI hỗ trợ không giới hạn
Trợ lý ảo tối ưu cho tương lai
Khám phá công nghệ AI tiên tiến
Công nghệ AI sáng tạo vượt trội
Trợ lý AI thông minh và mạnh mẽ
AI tối ưu hóa mọi công việc
Công cụ AI giúp bạn phát triển
Trợ lý ảo dẫn dắt mọi lĩnh vực
Khám phá trợ lý AI vượt trội
Công nghệ AI hỗ trợ toàn diện
Trợ lý AI thông minh cho mọi ngành
AI giúp bạn quản lý hiệu quả
Công cụ AI tối ưu hóa sáng tạo
Trợ lý ảo hỗ trợ không ngừng
Khám phá sức mạnh AI hiện đại
Công nghệ AI sáng tạo cho bạn
Trợ lý AI thông minh vượt mong đợi
AI tối ưu hóa cuộc sống hiện đại
Công cụ AI giúp bạn đi trước xu hướng
Trợ lý ảo dẫn đầu công nghệ
Khám phá công nghệ AI thông minh
Công nghệ AI hỗ trợ mọi lúc
Trợ lý AI thông minh cho doanh nhân
AI giúp bạn tối ưu hóa công việc
Công cụ AI sáng tạo và hiệu quả
Trợ lý ảo tối ưu cho mọi người
Khám phá trợ lý AI tiên phong
Công nghệ AI thay đổi cách sống
Trợ lý AI thông minh vượt trội
AI tối ưu hóa mọi quy trình
Công cụ AI giúp bạn thành công lớn
Trợ lý ảo dẫn dắt tương lai
Khám phá sức mạnh AI sáng tạo
Công nghệ AI hỗ trợ không giới hạn
Trợ lý AI thông minh cho mọi ngành
AI giúp bạn quản lý thời gian
Công cụ AI tối ưu cho sáng tạo
Trợ lý ảo hỗ trợ mọi nhu cầu
Khám phá công nghệ AI vượt bậc
Công nghệ AI sáng tạo cho bạn
Trợ lý AI thông minh và hiệu quả
AI tối ưu hóa công việc hàng ngày
Công cụ AI giúp bạn phát triển
Trợ lý ảo dẫn đầu mọi xu hướng
Khám phá trợ lý AI thông minh
Công nghệ AI hỗ trợ toàn diện
Trợ lý AI thông minh vượt mong đợi
AI giúp bạn đi trước thời đại
Công cụ AI tối ưu cho doanh nghiệp
Trợ lý ảo hỗ trợ không ngừng
Khám phá sức mạnh AI hiện đại
Công nghệ AI sáng tạo không ngừng
Trợ lý AI thông minh cho mọi người
AI tối ưu hóa mọi công việc
Công cụ AI giúp bạn thành công
Trợ lý ảo dẫn dắt mọi lĩnh vực
Khám phá công nghệ AI tiên tiến
Công nghệ AI hỗ trợ mọi lúc
Trợ lý AI thông minh vượt trội
AI giúp bạn quản lý hiệu quả
Công cụ AI tối ưu hóa sáng tạo
Trợ lý ảo hỗ trợ không giới hạn
Khám phá trợ lý AI sáng tạo
Công nghệ AI thay đổi cách sống
Trợ lý AI thông minh cho doanh nghiệp
AI tối ưu hóa công việc sáng tạo
Công cụ AI giúp bạn phát triển nhanh
Trợ lý ảo dẫn đầu công nghệ
Khám phá sức mạnh AI vượt bậc
Công nghệ AI sáng tạo cho bạn
Trợ lý AI thông minh và hiệu quả
AI giúp bạn đi trước xu hướng
Công cụ AI tối ưu cho mọi người
Trợ lý ảo hỗ trợ mọi lúc mọi nơi
Khám phá công nghệ AI thông minh
Công nghệ AI hỗ trợ không ngừng
Trợ lý AI thông minh vượt mong đợi
AI tối ưu hóa cuộc sống hiện đại
Công cụ AI giúp bạn thành công lớn
Trợ lý ảo dẫn dắt tương lai
Khám phá trợ lý AI tiên phong
Công nghệ AI sáng tạo vượt trội
Trợ lý AI thông minh cho doanh nhân
AI giúp bạn quản lý thời gian
Công cụ AI tối ưu hóa công việc
Trợ lý ảo hỗ trợ không giới hạn
Khám phá sức mạnh AI sáng tạo
Công nghệ AI thay đổi mọi ngành
Trợ lý AI thông minh và mạnh mẽ
AI tối ưu hóa mọi quy trình
Công cụ AI giúp bạn phát triển
Trợ lý ảo dẫn đầu mọi xu hướng
Khám phá công nghệ AI vượt trội
Công nghệ AI hỗ trợ toàn diện
Trợ lý AI thông minh cho mọi người
AI giúp bạn đi trước thời đại
Công cụ AI tối ưu cho sáng tạo
Trợ lý ảo hỗ trợ mọi lúc
Khám phá trợ lý AI thông minh
Công nghệ AI sáng tạo không ngừng
Trợ lý AI thông minh vượt bậc
AI tối ưu hóa công việc hiệu quả
Công cụ AI giúp bạn thành công
Trợ lý ảo dẫn dắt mọi lĩnh vực
Khám phá sức mạnh AI tiên phong
Công nghệ AI hỗ trợ không giới hạn
Trợ lý AI thông minh cho doanh nghiệp
AI giúp bạn quản lý hiệu quả
Công cụ AI tối ưu hóa sáng tạo
Trợ lý ảo hỗ trợ mọi nhu cầu
Khám phá công nghệ AI hiện đại
Công nghệ AI sáng tạo cho bạn
Trợ lý AI thông minh và hiệu quả
AI tối ưu hóa cuộc sống hàng ngày
Công cụ AI giúp bạn phát triển nhanh
Trợ lý ảo dẫn đầu công nghệ
Khám phá trợ lý AI vượt trội
Công nghệ AI hỗ trợ toàn diện
Trợ lý AI thông minh vượt mong đợi
AI giúp bạn đi trước xu hướng
Công cụ AI tối ưu cho doanh nghiệp
Trợ lý ảo hỗ trợ không ngừng
Khám phá sức mạnh AI sáng tạo
Công nghệ AI thay đổi mọi thứ
Trợ lý AI thông minh cho mọi người
AI tối ưu hóa mọi công việc
Công cụ AI giúp bạn thành công lớn
Trợ lý ảo dẫn dắt tương lai
Khám phá công nghệ AI tiên phong
Công nghệ AI sáng tạo vượt trội
Trợ lý AI thông minh và mạnh mẽ
AI giúp bạn quản lý thời gian
Công cụ AI tối ưu hóa công việc
Trợ lý ảo hỗ trợ không giới hạn
Khám phá trợ lý AI thông minh
Công nghệ AI hỗ trợ mọi lúc
Trợ lý AI thông minh vượt bậc
AI tối ưu hóa cuộc sống hiện đại
Công cụ AI giúp bạn phát triển
Trợ lý ảo dẫn đầu mọi xu hướng
Khám phá sức mạnh AI vượt trội
Công nghệ AI sáng tạo cho bạn
Trợ lý AI thông minh và hiệu quả
AI giúp bạn đi trước thời đại
Công cụ AI tối ưu cho sáng tạo
Trợ lý ảo hỗ trợ mọi nhu cầu
Khám phá công nghệ AI hiện đại
Công nghệ AI hỗ trợ không ngừng
Trợ lý AI thông minh vượt mong đợi
AI tối ưu hóa mọi quy trình
Công cụ AI giúp bạn thành công
Trợ lý ảo dẫn dắt mọi lĩnh vực
Khám phá trợ lý AI tiên phong
Công nghệ AI sáng tạo vượt trội
Trợ lý AI thông minh cho doanh nghiệp
AI giúp bạn quản lý hiệu quả
Công cụ AI tối ưu hóa sáng tạo
Trợ lý ảo hỗ trợ không giới hạn
Khám phá sức mạnh AI thông minh
Công nghệ AI thay đổi cách sống
Trợ lý AI thông minh và mạnh mẽ
AI tối ưu hóa công việc hàng ngày
Công cụ AI giúp bạn phát triển nhanh
Trợ lý ảo dẫn đầu công nghệ
Khám phá công nghệ AI vượt bậc
Công nghệ AI sáng tạo cho bạn
Trợ lý AI thông minh vượt trội
AI giúp bạn đi trước xu hướng
Công cụ AI tối ưu cho mọi người
Trợ lý ảo hỗ trợ mọi lúc mọi nơi
Khám phá trợ lý AI thông minh
Công nghệ AI hỗ trợ không ngừng
Trợ lý AI thông minh vượt mong đợi
AI tối ưu hóa cuộc sống hiện đại
Công cụ AI giúp bạn thành công lớn
Trợ lý ảo dẫn dắt tương lai
Khám phá sức mạnh AI tiên phong
Công nghệ AI sáng tạo vượt trội
Trợ lý AI thông minh cho doanh nhân
AI giúp bạn quản lý thời gian
Công cụ AI tối ưu hóa công việc
Trợ lý ảo hỗ trợ không giới hạn
Khám phá công nghệ AI hiện đại
Công nghệ AI hỗ trợ mọi lúc
Trợ lý AI thông minh vượt bậc
AI tối ưu hóa mọi quy trình
Công cụ AI giúp bạn phát triển
Trợ lý ảo dẫn đầu mọi xu hướng
Khám phá trợ lý AI thông minh
Công nghệ AI sáng tạo không ngừng
Trợ lý AI thông minh vượt trội
AI giúp bạn đi trước thời đại
Công cụ AI tối ưu cho sáng tạo
Trợ lý ảo hỗ trợ mọi nhu cầu
Khám phá sức mạnh AI vượt bậc
Công nghệ AI thay đổi cách sống
Trợ lý AI thông minh và hiệu quả
AI tối ưu hóa công việc hàng ngày
Công cụ AI giúp bạn thành công
Trợ lý ảo dẫn dắt mọi lĩnh vực
Khám phá công nghệ AI tiên phong
Công nghệ AI sáng tạo vượt trội
Trợ lý AI thông minh cho doanh nghiệp
AI giúp bạn quản lý hiệu quả
Công cụ AI tối ưu hóa sáng tạo
Trợ lý ảo hỗ trợ không ngừng
Khám phá trợ lý AI thông minh
Công nghệ AI hỗ trợ toàn diện
Trợ lý AI thông minh vượt mong đợi
AI tối ưu hóa mọi công việc
Công cụ AI giúp bạn phát triển nhanh
Trợ lý ảo dẫn đầu công nghệ
Khám phá sức mạnh AI sáng tạo
Công nghệ AI thay đổi mọi ngành
Trợ lý AI thông minh và mạnh mẽ
AI giúp bạn đi trước xu hướng
Công cụ AI tối ưu cho mọi người
Trợ lý ảo hỗ trợ mọi lúc mọi nơi
Khám phá công nghệ AI vượt trội
Công nghệ AI sáng tạo không ngừng
Trợ lý AI thông minh vượt bậc
AI tối ưu hóa cuộc sống hiện đại
Công cụ AI giúp bạn thành công lớn
Trợ lý ảo dẫn dắt tương lai
Khám phá trợ lý AI tiên phong
Công nghệ AI hỗ trợ không giới hạn
Trợ lý AI thông minh cho doanh nhân
AI giúp bạn quản lý thời gian
Công cụ AI tối ưu hóa công việc
Trợ lý ảo hỗ trợ mọi nhu cầu
Khám phá sức mạnh AI thông minh
Công nghệ AI sáng tạo cho bạn
Trợ lý AI thông minh và hiệu quả
AI tối ưu hóa mọi quy trình
Công cụ AI giúp bạn phát triển
Trợ lý ảo dẫn đầu mọi xu hướng
Khám phá công nghệ AI hiện đại
Công nghệ AI hỗ trợ toàn diện
Trợ lý AI thông minh vượt trội
AI giúp bạn đi trước thời đại
Công cụ AI tối ưu cho sáng tạo
Trợ lý ảo hỗ trợ không ngừng
Khám phá trợ lý AI thông minh
Công nghệ AI sáng tạo vượt bậc
Trợ lý AI thông minh cho doanh nghiệp
AI tối ưu hóa công việc hàng ngày
Công cụ AI giúp bạn thành công
Trợ lý ảo dẫn dắt mọi lĩnh vực
Khám phá sức mạnh AI tiên phong
Công nghệ AI hỗ trợ mọi lúc
Trợ lý AI thông minh vượt mong đợi
AI giúp bạn quản lý hiệu quả
Công cụ AI tối ưu hóa sáng tạo
Trợ lý ảo hỗ trợ không giới hạn
Khám phá công nghệ AI vượt trội
Công nghệ AI sáng tạo cho bạn
Trợ lý AI thông minh và mạnh mẽ
AI tối ưu hóa mọi công việc
Công cụ AI giúp bạn phát triển nhanh
Trợ lý ảo dẫn đầu công nghệ
Khám phá trợ lý AI thông minh
Công nghệ AI hỗ trợ không ngừng
Trợ lý AI thông minh vượt bậc
AI giúp bạn đi trước xu hướng
Công cụ AI tối ưu cho mọi người
Trợ lý ảo hỗ trợ mọi lúc mọi nơi
Khám phá sức mạnh AI sáng tạo
Công nghệ AI thay đổi cách sống
Trợ lý AI thông minh vượt trội
AI tối ưu hóa cuộc sống hiện đại
Công cụ AI giúp bạn thành công lớn
Trợ lý ảo dẫn dắt tương lai
Khám phá công nghệ AI tiên phong
Công nghệ AI sáng tạo không ngừng
Trợ lý AI thông minh cho doanh nhân
AI giúp bạn quản lý thời gian
Công cụ AI tối ưu hóa công việc
Trợ lý ảo hỗ trợ không giới hạn
Khám phá trợ lý AI thông minh
Công nghệ AI hỗ trợ mọi lúc
Trợ lý AI thông minh vượt mong đợi
AI tối ưu hóa mọi quy trình
Công cụ AI giúp bạn phát triển
Trợ lý ảo dẫn đầu mọi xu hướng
Khám phá sức mạnh AI vượt trội
Công nghệ AI sáng tạo cho bạn
Trợ lý AI thông minh và hiệu quả
AI giúp bạn đi trước thời đại
Công cụ AI tối ưu cho sáng tạo
Trợ lý ảo hỗ trợ mọi nhu cầu
Khám phá công nghệ AI hiện đại
Công nghệ AI hỗ trợ không ngừng
Trợ lý AI thông minh vượt bậc
AI tối ưu hóa công việc hàng ngày
Công cụ AI giúp bạn thành công
Trợ lý ảo dẫn dắt mọi lĩnh vực
Khám phá trợ lý AI tiên phong
Công nghệ AI sáng tạo vượt trội
Trợ lý AI thông minh cho doanh nghiệp
AI giúp bạn quản lý hiệu quả
Công cụ AI tối ưu hóa sáng tạo
Trợ lý ảo hỗ trợ không giới hạn
Khám phá sức mạnh AI thông minh
Công nghệ AI thay đổi cách sống
Trợ lý AI thông minh và mạnh mẽ
AI tối ưu hóa mọi công việc
Công cụ AI giúp bạn phát triển nhanh
Trợ lý ảo dẫn đầu công nghệ
Khám phá công nghệ AI vượt trội
Công nghệ AI sáng tạo cho bạn
Trợ lý AI thông minh vượt mong đợi
AI giúp bạn đi trước xu hướng
Công cụ AI tối ưu cho mọi người
Trợ lý ảo hỗ trợ mọi lúc mọi nơi
Khám phá trợ lý AI thông minh
Công nghệ AI hỗ trợ không ngừng
Trợ lý AI thông minh vượt bậc
AI tối ưu hóa cuộc sống hiện đại
Công cụ AI giúp bạn thành công lớn
Trợ lý ảo dẫn dắt tương lai
Khám phá sức mạnh AI tiên phong
Công nghệ AI sáng tạo vượt trội
Trợ lý AI thông minh cho doanh nhân
AI giúp bạn quản lý thời gian
Công cụ AI tối ưu hóa công việc
Trợ lý ảo hỗ trợ không giới hạn
Khám phá công nghệ AI hiện đại
Công nghệ AI hỗ trợ mọi lúc
Trợ lý AI thông minh vượt mong đợi
AI tối ưu hóa mọi quy trình
Công cụ AI giúp bạn phát triển
Trợ lý ảo dẫn đầu mọi xu hướng
Khám phá trợ lý AI thông minh
Công nghệ AI sáng tạo không ngừng
Trợ lý AI thông minh vượt trội
AI giúp bạn đi trước thời đại
Công cụ AI tối ưu cho sáng tạo
Trợ lý ảo hỗ trợ mọi nhu cầu
Khám phá sức mạnh AI vượt bậc
Công nghệ AI thay đổi cách sống
Trợ lý AI thông minh và hiệu quả
AI tối ưu hóa công việc hàng ngày
Công cụ AI giúp bạn thành công
Trợ lý ảo dẫn dắt mọi lĩnh vực
Khám phá công nghệ AI tiên phong
Công nghệ AI sáng tạo vượt trội
Trợ lý AI thông minh cho doanh nghiệp
AI giúp bạn quản lý hiệu quả
Công cụ AI tối ưu hóa sáng tạo
Trợ lý ảo hỗ trợ không giới hạn
Khám phá trợ lý AI thông minh
Công nghệ AI hỗ trợ toàn diện
Trợ lý AI thông minh vượt mong đợi
AI tối ưu hóa mọi công việc
Công cụ AI giúp bạn phát triển nhanh
Trợ lý ảo dẫn đầu công nghệ
Khám phá sức mạnh AI sáng tạo
Công nghệ AI thay đổi mọi ngành
Trợ lý AI thông minh và mạnh mẽ
AI giúp bạn đi trước xu hướng
Công cụ AI tối ưu cho mọi người
Trợ lý ảo hỗ trợ mọi lúc mọi nơi
Khám phá công nghệ AI vượt trội
Công nghệ AI sáng tạo không ngừng
Trợ lý AI thông minh vượt bậc
AI tối ưu hóa cuộc sống hiện đại
Công cụ AI giúp bạn thành công lớn
Trợ lý ảo dẫn dắt tương lai
Khám phá trợ lý AI tiên phong
Công nghệ AI hỗ trợ không giới hạn
Trợ lý AI thông minh cho doanh nhân
AI giúp bạn quản lý thời gian
Xổ số miền Nam Kết quả xổ số miền Nam XSMN hôm nay KQXS miền Nam XSMN trực tiếp KQXS hôm nay Xổ số kiến thiết miền Nam Dự đoán XSMN Xổ số miền Nam 24h XSMN chuẩn Kết quả xổ số nhanh Xổ số miền Nam hôm qua XSMN VIP Xổ số miền Nam 7 ngày Xổ số miền Nam chính xác XSMN 3 miền XSMN mới nhất Trực tiếp xổ số miền Nam Xổ số miền Nam hôm nay KQXS miền Nam chính xác Xổ số miền Nam hàng ngày Xổ số miền Nam nhanh nhất Dò vé số miền Nam Xổ số miền Nam chính thống Xổ số kiến thiết Kết quả xổ số miền Nam mới nhất XSMN cực nhanh Thống kê XSMN Dò xổ số miền Nam Xổ số online miền Nam KQXS hôm qua Xổ số nhanh nhất XSMN uy tín KQXS hôm nay nhanh nhất Dự đoán KQXS miền Nam Xổ số siêu tốc Xổ số VIP Xổ số miền Nam 30 ngày Lịch mở thưởng XSMN Xổ số hôm nay XSMN 2025 Dò vé số hôm nay Xổ số miền Nam miễn phí Trực tiếp KQXS miền Nam Dò xổ số nhanh Dự đoán XSMN chuẩn Xổ số 3 miền chính xác Thống kê xổ số miền Nam Dự đoán lô đề XSMN Kết quả XSMN online Kết quả xổ số 3 miền Dò xổ số VIP XSMN miễn phí Xổ số dễ trúng Xổ số miền Nam mỗi ngày Dự đoán XSMN hôm nay Thống kê kết quả XSMN Xổ số miền Nam hôm nay nhanh nhất Xổ số miền Nam mới nhất Xổ số miền Nam hôm qua Dự đoán xổ số miền Nam