I try to do it like this, but nothing happens:

var result = ""; someInput.onchange = function() { result = someInput.value; }; $.get("someapi", function (data) { result = data.foo; }); some.api.call(42, function (data) { result = data.bar; }); someDiv.textContent = result; 

For some someDiv nothing is displayed in someDiv .

3 answers 3

They waited! ES2017 8th edition .

Added description for functions with async modifier, and using await

The example already works in chrome:

 (async function() { var data = await fetch('https://jsonplaceholder.typicode.com/users'); console.log(await data.json()); })(); 


ES2015

This standard introduces the concept of a generator function - a function that can transfer control from the middle and then return to the same place. They are usually used to produce sequences.

 function* foo(){ yield 1; yield 2; while(true) yield 3; } 

This function returns an iterator for the sequence 1,2,3,3,3,... , which can be iterated. Although this is interesting in itself, but there is one specific case.

If the resulting sequence is a sequence of actions, not numbers, we can pause the function by starting the action each time and wait for the result before returning to the execution of the function. Thus, we obtain not a sequence of numbers, but a sequence of future values : i.e. promises .

This is somewhat more complicated, but a very powerful trick allows us to write asynchronous code in synchronous mode. There are several “runners” who do this. For example, Bluebird's Promise.coroutine will be used, but there are other Q.async like со or Q.async .

 var foo = coroutine(function*(){ var data = yield fetch("/echo/json"); // обратите внимание на yield // код здесь будет выполнен после получения ответа на запрос return data.json(); // data здесь определена }); 

This method also returns a promise that can be used in other coroutines. For example:

 var main = coroutine(function*(){ var bar = yield foo(); // ожидаем окончания нашей сопрограммы она вернет обещание // код ниже выполнится когда будет получен ответ от сервера var baz = yield fetch("/api/users/"+bar.userid); // зависит от результата возвращенного функцией foo console.log(baz); // выполнится когда завершатся оба запроса }); main(); 

ES2016 (ES7) The near future

There are hints in the standards for the introduction of new async keywords, await which would make it easier to work with promises.

 async function foo(){ var data = await fetch("/echo/json"); // обратите внимание на await // код тут выполнится только после выполнения запроса return data.json(); // data определена } 

But for now these are just reserved words and it is not known whether they will fall into the next standard and when they will be implemented.

At this point, you can use collectors to use them, such as Babel .

partial translation of this answer

  • five
    I would especially single out the option "the near future ", so that of all the options proposed in the answers, this is the only one that works in all cases and is the most readable . All the rest is either an attempt to hide the vermicelli (named functions), or it works only in trivial cases (promises). The future for TypeScript. - Athari
  • @Squidward, by the way in the last draft they are no longer just words :-) - Grundy

The problem is that the code does not have a wait operation. Neither a subscription to an event, nor an AJAX call, nor even an API call is waiting for the data to be received, but they immediately transfer control further. Therefore, the string someDiv.textContent = result; is executed BEFORE the result variable gets the value!

There are several ways to make this assignment after getting the value.

Method 0 - Move Assignment Inward

Perhaps this method looks like something stupid - but it solves the problem and is the easiest to understand. If your application is simple enough, this is how it should be done. See:

 someInput.onchange = function() { someDiv.textContent = someInput.value; }; $.get("someapi", function (data) { someDiv.textContent = data.foo; }); some.api.call(42, function (data) { someDiv.textContent = data.bar; }); someDiv.textContent = ""; 

In this case, I generally got rid of the variable result .

The disadvantage of this method is exactly 1 - there is no splitting into layers. Data is processed in the same place where it is obtained. If you feel that your scripts are becoming less and less understandable when using this method, or you have to write the same thing in several places, you need to switch to other methods.

Method 0+ - making assignment to a named function.

The simplest modification of the past method, which allows to get rid of code duplication.

 someInput.onchange = function() { setResult(someInput.value); }; $.get("someapi", function (data) { setResult(data.foo); }); some.api.call(42, function (data) { setResult(data.bar); }); setResult(""); function setResult(result) { someDiv.textContent = result; } 

I recall that in js function declarations "rise to the top", i.e. The setResult function declared at the bottom can be used anywhere. This allows you to start the script not with the declaration of 100500 functions - but with the code that will immediately begin to be executed.

This method is well suited for small scripts that are not divided into modules.

Macaroni problem

Sometimes, an asynchronous request is made in one module or its part, and its result must be obtained in another. Direct use of method 0+ leads to a code that is called "macaroni":

 // модуль 1 function getResult() { $.get("someapi", function (data) { setResult(data.foo); }); } // модуль 2 function someFunc() { getResult(); } function setResult(result) { someDiv.textContent = result; } 

Pay attention: someFunc calls getResult , which calls setResult . As a result, the two modules call each other. This is the macaroni code.

The methods below are intended to combat this code.

Method 1 - Callbacks (callbacks)

Add the function that makes the request, the callback parameter, to which we will pass the function that receives the answer:

 function getResult(callback) { $.get("someapi", function (data) { callback(data.foo); }); } 

Now this function can be called like this:

 getResult(function(result) { someDiv.textContent = result; }) 

Or like this:

 getResult(setResult); function setResult(result) { someDiv.textContent = result; } 

Method 2 - promises (promises)

A promise in js is a programming pattern that indicates a value that is not currently there, but it is expected to be in the future.

There are several promises implemented. The main are now ES6 Promises , they are supported by modern browsers except IE. (But for those browsers that do not support them, there is a bunch of polyfiles ).

Promises are created like this:

 function getResult(N) { return new Promise(function (resolve, reject) { some.api.call(N, function (data) { resolve(data.bar); }); }); } 

You can also use jQuery Deferred as a promise:

 function getResult(N) { var d = $.Deferred(); some.api.call(N, function (data) { d.resolve(data.bar); }); return d.promise(); } 

Or Angular $ q :

 function getResult(N) { var d = $q.defer(); some.api.call(N, function (data) { d.resolve(data.bar); }); return d.promise; } 

By the way, Angular $ q can be used like es6 promise:

 function getResult(N) { return $q(function (resolve, reject) { some.api.call(N, function (data) { resolve(data.bar); }); }); } 

In any case, using this getResult function will look the same:

 getResult(42).then(function (result) { someDiv.textContent = result; }); 

Alternatively, you can use the new syntax async / await, described in the answer below from Grundy

I draw attention to the fact that here I took some.api.call as an some.api.call , but not an event or an ajax call — and this is not by accident!

The fact is that a promise can be fulfilled ( resolved ) only once, and most of the events occur several times. Therefore, to use promises for the same onchanged - it is impossible.

As for the ajax call, we must remember that it ALREADY returns the promise! And because all the ways above in combination with it will look ridiculous. Everything is made much easier:

 function getResult() { return $.get("someapi") .then(function (data) { return data.foo; }); } 

By the way, here too it was possible to use async / await

In case you get confused in the code above, here is its "unwrapped" version:

 function getResult() { var q1 = $.get("someapi"); var q2 = q1.then(function (data) { return data.foo; }); return q2; } 

It's simple. By itself, the $.get returns a promise that, when executed, will contain data pinched from the server.

Next, we create a continuation for it, which will process this data (get the foo field).

Well, then this continuation (which is also a promise) is what we return.

Method 3 — Observed values ​​(observables) in Knockout

Usually, Knockout is remembered as a library for two-way data binding to a view - but its capabilities can also be useful in solving such problems.

You can do so. First, let's get the observed value:

 var result = ko.observable(""); 

This value can be changed by event:

 someInput.onchange = function() { // вызов result с параметром устанавливает значение равным параметру result(someInput.value); }; 

And now you can execute some block of code every time this value changes:

 ko.computed(function() { // вызов result без параметров возвращает текущее значение someDiv.textContent = result(); }); 

The function passed to ko.computed will be called each time its dependencies change.

The PS code above is shown as an example of manual work with observable values. But keep in mind that Knockout has easier ways to work with the contents of DOM elements:

 var vm = { result: ko.observable() }; ko.applyBindings(vm); 
 <input data-bind="value: result"></input> <!-- бывший someInput --> <div data-bind="text: result"></div> <!-- бывший someDiv --> 

Method 3.1 — Observables in MobX

Everything is almost the same as in knockout. In the example below, I use the syntax of ES2016 and later, because the library involves the use of new language features:

 import { observable, autorun } from 'mobx'; var result = observable(""); someInput.onchange = () => { result.set(someInput.value); }; autorun(() => someDiv.textContent = result.get()); 

However, MobX usually uses classes, not single obervables:

 class ViewModel { @observable result = ""; } var vm = new ViewModel(); someInput.onchange = () => { vm.result = someInput.value; }; autorun(() => someDiv.textContent = vm.result); 
  • rxjs can be for completeness more .. - Vladimir Gamalyan
  • 2
    async / await .... - Vladimir Gamalyan
  • fibers / green threads .. - Vladimir Gamalyan
  • @VladimirGamalian Is async / await already supported? - Pavel Mayorov
  • 2
    @VladimirGamalian, async / await so far even in the standard is not the last, only await as a reserved keyword for the future - Grundy

Code with asynchronous functions can be executed synchronously using the alternative JS engine nsynjs

If the asynchronous function returns promise

then we simply call the function, and the value of the promise is obtained through 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 asynchronous function calls callback

Step 1. Wrap the asynchronous function in the nsynjs wrapper (or in the promise):

 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 the logic in the function, as if the logic was executed synchronously

 function process() { console.log('got data:', ajaxGet(nsynjsCtx, "data/file1.json").data); } 

Step 3. We execute function through nsynjs

 nsynjs.run(process,this,function () { console.log("synchronous function finished"); }); 

Nsynjs will sequentially execute the function code, stopping and waiting for the result of calls to all the asynchronous functions.