为什么我的变量在函数内部修改后不变? - 异步代码参考

发布于 2025-02-11 15:00:17 字数 1437 浏览 1 评论 0 原文

给定以下示例,为什么在所有情况下 outerscopevar 不确定?

var outerScopeVar;

var img = document.createElement('img');
img.onload = function() {
    outerScopeVar = this.width;
};
img.src = 'lolcat.png';
alert(outerScopeVar);
var outerScopeVar;
setTimeout(function() {
    outerScopeVar = 'Hello Asynchronous World!';
}, 0);
alert(outerScopeVar);
// Example using some jQuery
var outerScopeVar;
$.post('loldog', function(response) {
    outerScopeVar = response;
});
alert(outerScopeVar);
// Node.js example
var outerScopeVar;
fs.readFile('./catdog.html', function(err, data) {
    outerScopeVar = data;
});
console.log(outerScopeVar);
// with promises
var outerScopeVar;
myPromise.then(function (response) {
    outerScopeVar = response;
});
console.log(outerScopeVar);
// with observables
var outerScopeVar;
myObservable.subscribe(function (value) {
    outerScopeVar = value;
});
console.log(outerScopeVar);
// geolocation API
var outerScopeVar;
navigator.geolocation.getCurrentPosition(function (pos) {
    outerScopeVar = pos;
});
console.log(outerScopeVar);

为什么在所有这些示例中输出未定义的?我不想解决方法,我想知道为什么发生这种情况。


注意:这是 javaScript异步性的规范问题。随意改善这个问题并添加社区可以认同的更简化的示例。

Given the following examples, why is outerScopeVar undefined in all cases?

var outerScopeVar;

var img = document.createElement('img');
img.onload = function() {
    outerScopeVar = this.width;
};
img.src = 'lolcat.png';
alert(outerScopeVar);
var outerScopeVar;
setTimeout(function() {
    outerScopeVar = 'Hello Asynchronous World!';
}, 0);
alert(outerScopeVar);
// Example using some jQuery
var outerScopeVar;
$.post('loldog', function(response) {
    outerScopeVar = response;
});
alert(outerScopeVar);
// Node.js example
var outerScopeVar;
fs.readFile('./catdog.html', function(err, data) {
    outerScopeVar = data;
});
console.log(outerScopeVar);
// with promises
var outerScopeVar;
myPromise.then(function (response) {
    outerScopeVar = response;
});
console.log(outerScopeVar);
// with observables
var outerScopeVar;
myObservable.subscribe(function (value) {
    outerScopeVar = value;
});
console.log(outerScopeVar);
// geolocation API
var outerScopeVar;
navigator.geolocation.getCurrentPosition(function (pos) {
    outerScopeVar = pos;
});
console.log(outerScopeVar);

Why does it output undefined in all of these examples? I don't want workarounds, I want to know why this is happening.


Note: This is a canonical question for JavaScript asynchronicity. Feel free to improve this question and add more simplified examples which the community can identify with.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

盛夏已如深秋| 2025-02-18 15:00:17

一个词答案:异步性

前言

这个话题在堆栈溢出中至少迭代了几千次。因此,首先,我想指出一些非常有用的资源:


手头问题的答案

让我们首先追踪共同行为。在所有示例中, outerscopevar 函数内进行了修改。该功能显然不会立即执行;它是作为参数分配或通过的。这就是我们所说的 回调

现在的问题是,该回调是什么时候打来的?

这取决于情况。让我们尝试再次追踪一些常见的行为:

  • img.onload 可以在将来的某个时候称为何时(以及如果)已成功加载图像。
  • settimeout 可以在将来的某个时候称为 过期,并且未通过 clear> clear> clear> cleartimeout 取消超时。注意:即使使用 0 作为延迟,所有浏览器也具有最小超时延迟帽(在HTML5规格中指定为4ms)。
  • jQuery $。POST 的回调可以在将来的某个时候称为何时(以及IF)AJAX请求已成功完成。
  • Node.js的 fs.ReadFile 在将来的某个时候可以称为 成功读取文件或丢弃错误。

在所有情况下,我们都有一个回调,可以在以后的某个时候运行。我们将这种“将来的某个时候”称为异步流

异步执行将从同步流中推出。也就是说,在同步代码堆栈执行时,异步代码将永远不会执行。这就是JavaScript被单线读取的含义。

更具体地说,当JS引擎闲置时 - 不执行(a)同步代码的堆栈 - 它将对可能触发异步回调的事件进行轮询(例如,过期的超时,接收到网络响应)并又一次地执行它们。这被认为是事件loop

也就是说,手绘红色形状中突出显示的异步代码只有在其各自的代码块中的所有剩余同步代码执行后才能执行:

“

简而言之,回调函数是同步创建但异步执行的。您不能依靠执行异步函数,直到您知道它已被执行,以及如何做到这一点?

真的很简单。取决于异步函数执行的逻辑应从此异步函数内部启动/调用。例如,在回调函数中移动警报 s和 console.log s将输出预期的结果,因为结果在此点可用。

通常,实现自己的回调逻辑,

您需要通过异步功能的结果做更多​​的事情,或者根据调用异步功能的位置,而将结果做不同的事情。让我们解决一个更复杂的示例:

var outerScopeVar;
helloCatAsync();
alert(outerScopeVar);

function helloCatAsync() {
    setTimeout(function() {
        outerScopeVar = 'Nya';
    }, Math.random() * 2000);
}

注意:我正在使用 settimeout 随机延迟作为通用异步函数;同一示例适用于Ajax, ReadFile onload 和任何其他异步流。

这个示例显然与其他示例相同的问题;直到异步函数执行之前,它才等待。

让我们通过实现自己的回调系统来解决它。首先,我们摆脱了那个丑陋的 outerscopevar ,在这种情况下是完全无用的。然后,我们添加一个接受函数参数的参数,即我们的回调。当异步操作完成时,我们将调用此回调,通过结果。实现(请按顺序阅读评论):

// 1. Call helloCatAsync passing a callback function,
//    which will be called receiving the result from the async operation
helloCatAsync(function(result) {
    // 5. Received the result from the async function,
    //    now do whatever you want with it:
    alert(result);
});

// 2. The "callback" parameter is a reference to the function which
//    was passed as an argument from the helloCatAsync call
function helloCatAsync(callback) {
    // 3. Start async operation:
    setTimeout(function() {
        // 4. Finished async operation,
        //    call the callback, passing the result as an argument
        callback('Nya');
    }, Math.random() * 2000);
}

上述示例的代码段:

// 1. Call helloCatAsync passing a callback function,
//    which will be called receiving the result from the async operation
console.log("1. function called...")
helloCatAsync(function(result) {
    // 5. Received the result from the async function,
    //    now do whatever you want with it:
    console.log("5. result is: ", result);
});

// 2. The "callback" parameter is a reference to the function which
//    was passed as an argument from the helloCatAsync call
function helloCatAsync(callback) {
    console.log("2. callback here is the function passed as argument above...")
    // 3. Start async operation:
    setTimeout(function() {
    console.log("3. start async operation...")
    console.log("4. finished async operation, calling the callback, passing the result...")
        // 4. Finished async operation,
        //    call the callback passing the result as argument
        callback('Nya');
    }, Math.random() * 2000);
}

在实际用例中,大多数情况下,DOM API和大多数库已经提供了回调功能(在此示例示例中 Hellocatasync 实现)。您只需要传递回调函数,并了解它将从同步流中执行并重组代码以适应它。

您还会注意到,由于异步性质,不可能返回从异步流回到定义回调的同步流中的值,因为异步回调是在同步之后执行的,代码已经完成执行。

您必须使用回调模式或...承诺,而不是返回从异步回调中获得值。

承诺

虽然有多种方法可以保持“ noreferrer”> callback地狱与Vanilla JS一起在BAY中,承诺正在越来越受欢迎,目前正在标准化在ES6中(请参见 promise -promise -promise -mdn )。

Promises(又名期货)提供了对异步代码的更线性,因此令人愉悦的阅读,但是解释它们的整个功能不超出此问题的范围。取而代之的是,我将为有兴趣的人留下这些优质的资源:


性的更多阅读材料

  • 有关JavaScript异步 >使用香草JS示例和node.js代码很好地说明异步代码和回调。

注意:我将这个答案标记为社区Wiki。因此,至少有100个声誉的人都可以编辑和改进它!如果您愿意,请随时提高此答案或提交全新的答案。

我想将这个问题变成一个与Ajax无关的异步问题的规范主题(有因此,从Ajax的电话中?

One word answer: asynchronicity.

Forewords

This topic has been iterated at least a couple of thousands of times here in Stack Overflow. Hence, first off I'd like to point out some extremely useful resources:


The answer to the question at hand

Let's trace the common behavior first. In all examples, the outerScopeVar is modified inside of a function. That function is clearly not executed immediately; it is being assigned or passed as an argument. That is what we call a callback.

Now the question is, when is that callback called?

It depends on the case. Let's try to trace some common behavior again:

  • img.onload may be called sometime in the future when (and if) the image has successfully loaded.
  • setTimeout may be called sometime in the future after the delay has expired and the timeout hasn't been canceled by clearTimeout. Note: even when using 0 as delay, all browsers have a minimum timeout delay cap (specified to be 4ms in the HTML5 spec).
  • jQuery $.post's callback may be called sometime in the future when (and if) the Ajax request has been completed successfully.
  • Node.js's fs.readFile may be called sometime in the future when the file has been read successfully or thrown an error.

In all cases, we have a callback that may run sometime in the future. This "sometime in the future" is what we refer to as asynchronous flow.

Asynchronous execution is pushed out of the synchronous flow. That is, the asynchronous code will never execute while the synchronous code stack is executing. This is the meaning of JavaScript being single-threaded.

More specifically, when the JS engine is idle -- not executing a stack of (a)synchronous code -- it will poll for events that may have triggered asynchronous callbacks (e.g. expired timeout, received network response) and execute them one after another. This is regarded as Event Loop.

That is, the asynchronous code highlighted in the hand-drawn red shapes may execute only after all the remaining synchronous code in their respective code blocks have executed:

async code highlighted

In short, the callback functions are created synchronously but executed asynchronously. You can't rely on the execution of an asynchronous function until you know it has been executed, and how to do that?

It is simple, really. The logic that depends on the asynchronous function execution should be started/called from inside this asynchronous function. For example, moving the alerts and console.logs inside the callback function would output the expected result because the result is available at that point.

Implementing your own callback logic

Often you need to do more things with the result from an asynchronous function or do different things with the result depending on where the asynchronous function has been called. Let's tackle a bit more complex example:

var outerScopeVar;
helloCatAsync();
alert(outerScopeVar);

function helloCatAsync() {
    setTimeout(function() {
        outerScopeVar = 'Nya';
    }, Math.random() * 2000);
}

Note: I'm using setTimeout with a random delay as a generic asynchronous function; the same example applies to Ajax, readFile, onload, and any other asynchronous flow.

This example clearly suffers from the same issue as the other examples; it is not waiting until the asynchronous function executes.

Let's tackle it by implementing a callback system of our own. First off, we get rid of that ugly outerScopeVar which is completely useless in this case. Then we add a parameter that accepts a function argument, our callback. When the asynchronous operation finishes, we call this callback, passing the result. The implementation (please read the comments in order):

// 1. Call helloCatAsync passing a callback function,
//    which will be called receiving the result from the async operation
helloCatAsync(function(result) {
    // 5. Received the result from the async function,
    //    now do whatever you want with it:
    alert(result);
});

// 2. The "callback" parameter is a reference to the function which
//    was passed as an argument from the helloCatAsync call
function helloCatAsync(callback) {
    // 3. Start async operation:
    setTimeout(function() {
        // 4. Finished async operation,
        //    call the callback, passing the result as an argument
        callback('Nya');
    }, Math.random() * 2000);
}

Code snippet of the above example:

// 1. Call helloCatAsync passing a callback function,
//    which will be called receiving the result from the async operation
console.log("1. function called...")
helloCatAsync(function(result) {
    // 5. Received the result from the async function,
    //    now do whatever you want with it:
    console.log("5. result is: ", result);
});

// 2. The "callback" parameter is a reference to the function which
//    was passed as an argument from the helloCatAsync call
function helloCatAsync(callback) {
    console.log("2. callback here is the function passed as argument above...")
    // 3. Start async operation:
    setTimeout(function() {
    console.log("3. start async operation...")
    console.log("4. finished async operation, calling the callback, passing the result...")
        // 4. Finished async operation,
        //    call the callback passing the result as argument
        callback('Nya');
    }, Math.random() * 2000);
}

Most often in real use cases, the DOM API and most libraries already provide the callback functionality (the helloCatAsync implementation in this demonstrative example). You only need to pass the callback function and understand that it will execute out of the synchronous flow and restructure your code to accommodate for that.

You will also notice that due to the asynchronous nature, it is impossible to return a value from an asynchronous flow back to the synchronous flow where the callback was defined, as the asynchronous callbacks are executed long after the synchronous code has already finished executing.

Instead of returning a value from an asynchronous callback, you will have to make use of the callback pattern, or... Promises.

Promises

Although there are ways to keep the callback hell at bay with vanilla JS, promises are growing in popularity and are currently being standardized in ES6 (see Promise - MDN).

Promises (a.k.a. Futures) provide a more linear, and thus pleasant, reading of the asynchronous code, but explaining their entire functionality is out of the scope of this question. Instead, I'll leave these excellent resources for the interested:


More reading material about JavaScript asynchronicity


Note: I've marked this answer as Community Wiki. Hence anyone with at least 100 reputations can edit and improve it! Please feel free to improve this answer or submit a completely new answer if you'd like as well.

I want to turn this question into a canonical topic to answer asynchronicity issues that are unrelated to Ajax (there is How to return the response from an AJAX call? for that), hence this topic needs your help to be as good and helpful as possible!

夜唯美灬不弃 2025-02-18 15:00:17

fabrício的答案是现实的;但是我想将他的答案与较少的技术相辅相成,这是一个类比,以帮助解释异步性的概念


一个类比...

昨天,我所做的工作需要同事的一些信息。我把他赶了起来。这是对话的方式:

me :嗨,鲍勃,我需要知道上周的 foo 'd bar 'd。吉姆想要一份报告,而您是唯一知道有关它的细节的人。

bob :当然可以,但是我大约需要30分钟?

me :真是太棒了。当您获得信息时,请给我一个戒指!

此时,我挂断了电话。由于我需要鲍勃(Bob)的信息来完成报告,因此我离开了报告,然后去喝咖啡,然后我赶上了一些电子邮件。 40分钟后(鲍勃很慢),鲍勃回电,给了我所需的信息。在这一点上,我恢复了报告,因为我拥有所需的所有信息。


想象一下,如果谈话是这样的;

me :嗨,鲍勃,我需要知道上周的 foo 'd bar 'd。吉姆(Jim Want)是关于它的报告,而您是唯一知道有关它的细节的人。

bob :当然可以,但是我大约需要30分钟?

me :真是太棒了。我会等。

我坐在那里等了。并等待。并等待。 40分钟。除了等待。最终,鲍勃给了我信息,我们挂了电话,我完成了报告。但是我失去了40分钟的生产力。


这是异步与同步行为,

这正是我们问题中所有示例中正在发生的事情。加载图像,从磁盘上加载文件并通过Ajax请求页面都是缓慢的操作(在现代计算的背景下)。

JavaScript允许您注册一个回调函数,而不是等待这些慢操作完成,而不是等待这些慢速操作,该功能将在慢速操作完成后执行。但是,与此同时,JavaScript将继续执行其他代码。 JavaScript执行其他代码的事实在等待缓慢的操作完成时,行为异步。如果JavaScript在执行任何其他代码之前就等待了操作完成操作,那么这将是同步行为。

var outerScopeVar;    
var img = document.createElement('img');

// Here we register the callback function.
img.onload = function() {
    // Code within this function will be executed once the image has loaded.
    outerScopeVar = this.width;
};

// But, while the image is loading, JavaScript continues executing, and
// processes the following lines of JavaScript.
img.src = 'lolcat.png';
alert(outerScopeVar);

在上面的代码中,我们要求JavaScript加载 lolcat.png ,即 sloooow 操作。此缓慢操作完成后将执行回调函数,但是与此同时,JavaScript将继续处理下一行代码。 IE alert(OUTERSCOPEVAR)

这就是为什么我们看到警报显示未定义的原因;由于 alert()立即处理,而不是加载图像之后。

为了修复我们的代码,我们要做的就是将 alert(OUTersCopevar)代码移动到回调函数中。因此,我们不再需要 outerscopevar 变量称为全局变量。

var img = document.createElement('img');

img.onload = function() {
    var localScopeVar = this.width;
    alert(localScopeVar);
};

img.src = 'lolcat.png';

您将 始终 请参阅“回调”为函数,因为这是JavaScript中唯一的*定义某些代码的方法,但直到以后才能执行它。

因此,在我们的所有示例中, function(){/ *做一些 */} 是回调。为了解决所有示例,我们要做的就是将需要操作响应的代码移动到其中!

*从技术上讲,您也可以使用 eRAT(),但是 evar()是邪恶的为此目的


如何让呼叫者等待?

您目前可能有一些与此类似的代码;

function getWidthOfImage(src) {
    var outerScopeVar;

    var img = document.createElement('img');
    img.onload = function() {
        outerScopeVar = this.width;
    };
    img.src = src;
    return outerScopeVar;
}

var width = getWidthOfImage('lolcat.png');
alert(width);

但是,我们现在知道返回OutersCopevar 立即发生。在 onload 回调函数之前已更新了变量。这导致 getWidThoFimage()返回 undefined 未定义的被警报。

要解决此问题,我们需要允许函数调用 getWidThoFimage()注册回调,然后将宽度的警报移动到该回调内;

function getWidthOfImage(src, cb) {     
    var img = document.createElement('img');
    img.onload = function() {
        cb(this.width);
    };
    img.src = src;
}

getWidthOfImage('lolcat.png', function (width) {
    alert(width);
});

...和以前一样,请注意,我们已经能够删除全局变量(在这种情况下 width )。

Fabrício's answer is spot on; but I wanted to complement his answer with something less technical, which focusses on an analogy to help explain the concept of asynchronicity.


An Analogy...

Yesterday, the work I was doing required some information from a colleague. I rang him up; here's how the conversation went:

Me: Hi Bob, I need to know how we foo'd the bar'd last week. Jim wants a report on it, and you're the only one who knows the details about it.

Bob: Sure thing, but it'll take me around 30 minutes?

Me: That's great Bob. Give me a ring back when you've got the information!

At this point, I hung up the phone. Since I needed information from Bob to complete my report, I left the report and went for a coffee instead, then I caught up on some email. 40 minutes later (Bob is slow), Bob called back and gave me the information I needed. At this point, I resumed my work with my report, as I had all the information I needed.


Imagine if the conversation had gone like this instead;

Me: Hi Bob, I need to know how we foo'd the bar'd last week. Jim want's a report on it, and you're the only one who knows the details about it.

Bob: Sure thing, but it'll take me around 30 minutes?

Me: That's great Bob. I'll wait.

And I sat there and waited. And waited. And waited. For 40 minutes. Doing nothing but waiting. Eventually, Bob gave me the information, we hung up, and I completed my report. But I'd lost 40 minutes of productivity.


This is asynchronous vs. synchronous behavior

This is exactly what is happening in all the examples in our question. Loading an image, loading a file off disk, and requesting a page via AJAX are all slow operations (in the context of modern computing).

Rather than waiting for these slow operations to complete, JavaScript lets you register a callback function which will be executed when the slow operation has completed. In the meantime, however, JavaScript will continue to execute other code. The fact that JavaScript executes other code whilst waiting for the slow operation to complete makes the behaviorasynchronous. Had JavaScript waited around for the operation to complete before executing any other code, this would have been synchronous behavior.

var outerScopeVar;    
var img = document.createElement('img');

// Here we register the callback function.
img.onload = function() {
    // Code within this function will be executed once the image has loaded.
    outerScopeVar = this.width;
};

// But, while the image is loading, JavaScript continues executing, and
// processes the following lines of JavaScript.
img.src = 'lolcat.png';
alert(outerScopeVar);

In the code above, we're asking JavaScript to load lolcat.png, which is a sloooow operation. The callback function will be executed once this slow operation has done, but in the meantime, JavaScript will keep processing the next lines of code; i.e. alert(outerScopeVar).

This is why we see the alert showing undefined; since the alert() is processed immediately, rather than after the image has been loaded.

In order to fix our code, all we have to do is move the alert(outerScopeVar) code into the callback function. As a consequence of this, we no longer need the outerScopeVar variable declared as a global variable.

var img = document.createElement('img');

img.onload = function() {
    var localScopeVar = this.width;
    alert(localScopeVar);
};

img.src = 'lolcat.png';

You'll always see a callback is specified as a function, because that's the only* way in JavaScript to define some code, but not execute it until later.

Therefore, in all of our examples, the function() { /* Do something */ } is the callback; to fix all the examples, all we have to do is move the code which needs the response of the operation into there!

* Technically you can use eval() as well, but eval() is evil for this purpose


How do I keep my caller waiting?

You might currently have some code similar to this;

function getWidthOfImage(src) {
    var outerScopeVar;

    var img = document.createElement('img');
    img.onload = function() {
        outerScopeVar = this.width;
    };
    img.src = src;
    return outerScopeVar;
}

var width = getWidthOfImage('lolcat.png');
alert(width);

However, we now know that the return outerScopeVar happens immediately; before the onload callback function has updated the variable. This leads to getWidthOfImage() returning undefined, and undefined being alerted.

To fix this, we need to allow the function calling getWidthOfImage() to register a callback, then move the alert'ing of the width to be within that callback;

function getWidthOfImage(src, cb) {     
    var img = document.createElement('img');
    img.onload = function() {
        cb(this.width);
    };
    img.src = src;
}

getWidthOfImage('lolcat.png', function (width) {
    alert(width);
});

... as before, note that we've been able to remove the global variables (in this case width).

十秒萌定你 2025-02-18 15:00:17

这是一个更简洁的答案,对于正在寻求快速参考的人们以及使用Promises和异步/等待的一些示例的人。

从调用异步方法的函数(在这种情况下为 settimeout )开始,从幼稚的方法(无效)开始,然后返回一条消息:

function getMessage() {
  var outerScopeVar;
  setTimeout(function() {
    outerScopeVar = 'Hello asynchronous world!';
  }, 0);
  return outerScopeVar;
}
console.log(getMessage());

undectined 在此记录情况是因为 getMessage 在调用 settimeout 回调之前返回并更新 outerscopevar

解决它的两种主要方法是使用回调承诺

回调

这里的更改是 getMessage 接受回调参数将被调用,以将结果传递回通话代码。

function getMessage(callback) {
  setTimeout(function() {
    callback('Hello asynchronous world!');
  }, 0);
}
getMessage(function(message) {
  console.log(message);
});

承诺

回调是因为它们可以自然合并以协调多个异步操作。 a promises/a+标准实现在node.js(0.12+)和许多当前浏览器中提供,但也实现了在 q

function getMessage() {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      resolve('Hello asynchronous world!');
    }, 0);
  });
}

getMessage().then(function(message) {
  console.log(message);  
});

deferreds

jQuery 延期。

function getMessage() {
  var deferred = $.Deferred();
  setTimeout(function() {
    deferred.resolve('Hello asynchronous world!');
  }, 0);
  return deferred.promise();
}

getMessage().done(function(message) {
  console.log(message);  
});

async/等待

如果您的javascript环境包括 async 等待 (例如Node.js 7.6+),然后您可以在 async function中同步使用承诺:

function getMessage () {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            resolve('Hello asynchronous world!');
        }, 0);
    });
}

async function main() {
    let message = await getMessage();
    console.log(message);
}

main();

Here's a more concise answer for people that are looking for a quick reference as well as some examples using promises and async/await.

Start with the naive approach (that doesn't work) for a function that calls an asynchronous method (in this case setTimeout) and returns a message:

function getMessage() {
  var outerScopeVar;
  setTimeout(function() {
    outerScopeVar = 'Hello asynchronous world!';
  }, 0);
  return outerScopeVar;
}
console.log(getMessage());

undefined gets logged in this case because getMessage returns before the setTimeout callback is called and updates outerScopeVar.

The two main ways to solve it are using callbacks and promises:

Callbacks

The change here is that getMessage accepts a callback parameter that will be called to deliver the results back to the calling code once available.

function getMessage(callback) {
  setTimeout(function() {
    callback('Hello asynchronous world!');
  }, 0);
}
getMessage(function(message) {
  console.log(message);
});

Promises

Promises provide an alternative which is more flexible than callbacks because they can be naturally combined to coordinate multiple async operations. A Promises/A+ standard implementation is natively provided in node.js (0.12+) and many current browsers, but is also implemented in libraries like Bluebird and Q.

function getMessage() {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      resolve('Hello asynchronous world!');
    }, 0);
  });
}

getMessage().then(function(message) {
  console.log(message);  
});

jQuery Deferreds

jQuery provides functionality that's similar to promises with its Deferreds.

function getMessage() {
  var deferred = $.Deferred();
  setTimeout(function() {
    deferred.resolve('Hello asynchronous world!');
  }, 0);
  return deferred.promise();
}

getMessage().done(function(message) {
  console.log(message);  
});

async/await

If your JavaScript environment includes support for async and await (like Node.js 7.6+), then you can use promises synchronously within async functions:

function getMessage () {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            resolve('Hello asynchronous world!');
        }, 0);
    });
}

async function main() {
    let message = await getMessage();
    console.log(message);
}

main();
z祗昰~ 2025-02-18 15:00:17

要陈述显而易见的杯子,杯子代表 outerscopevar

异步函数就像...

To state the obvious, the cup represents outerScopeVar.

Asynchronous functions be like...

asynchronous call for coffee

娇纵 2025-02-18 15:00:17

其他答案非常好,我只想为此提供直接的答案。仅限于jQuery异步调用

所有ajax调用(包括 $。get $。post 或 $。$。ajax )是异步的。

考虑到您的示例,

var outerScopeVar;  //line 1
$.post('loldog', function(response) {  //line 2
    outerScopeVar = response;
});
alert(outerScopeVar);  //line 3

代码执行始于第1行,在第2行(即,邮政请求)上声明变量和触发器以及异步调用,并继续从第3行执行,而无需等待邮政请求以完成其执行。

假设邮政请求需要10秒才能完成, outerscopevar 的值才能在那10秒之后设置。

要尝试一下,

var outerScopeVar; //line 1
$.post('loldog', function(response) {  //line 2, takes 10 seconds to complete
    outerScopeVar = response;
});
alert("Lets wait for some time here! Waiting is fun");  //line 3
alert(outerScopeVar);  //line 4

现在执行此操作时,您将在第3行上获得警报。现在等待一段时间,直到您确定发布请求已返回一些值。然后,当您单击“确定”时,在警报框上,下一个警报将打印预期值,因为您需要等待它。

在现实生活中,代码变为

var outerScopeVar;
$.post('loldog', function(response) {
    outerScopeVar = response;
    alert(outerScopeVar);
});

所有取决于异步调用的代码,都会在异步块中移动或通过等待异步调用。

The other answers are excellent and I just want to provide a straight forward answer to this. Just limiting to jQuery asynchronous calls

All ajax calls (including the $.get or $.post or $.ajax) are asynchronous.

Considering your example

var outerScopeVar;  //line 1
$.post('loldog', function(response) {  //line 2
    outerScopeVar = response;
});
alert(outerScopeVar);  //line 3

The code execution starts from line 1, declares the variable and triggers and asynchronous call on line 2, (i.e., the post request) and it continues its execution from line 3, without waiting for the post request to complete its execution.

Lets say that the post request takes 10 seconds to complete, the value of outerScopeVar will only be set after those 10 seconds.

To try out,

var outerScopeVar; //line 1
$.post('loldog', function(response) {  //line 2, takes 10 seconds to complete
    outerScopeVar = response;
});
alert("Lets wait for some time here! Waiting is fun");  //line 3
alert(outerScopeVar);  //line 4

Now when you execute this, you would get an alert on line 3. Now wait for some time until you are sure the post request has returned some value. Then when you click OK, on the alert box, next alert would print the expected value, because you waited for it.

In real life scenario, the code becomes,

var outerScopeVar;
$.post('loldog', function(response) {
    outerScopeVar = response;
    alert(outerScopeVar);
});

All the code that depends on the asynchronous calls, is moved inside the asynchronous block, or by waiting on the asynchronous calls.

人间☆小暴躁 2025-02-18 15:00:17

在所有这些情况下执行不会等待

1。

​'lolcat.png'; 和 alert(outerscopevar); 同时可能不会发生事件。即,funtion img.onload 等待引用的图像加载,异步。这将发生所有令人兴奋的例子 - 事件可能会有所不同。

2。

​这里是 0 ,但仍会注册一个异步事件,它将添加到 Event Queue Queue 执行的最后位置,这使保证的延迟。

3。

”在此处输入图像说明”
这次是Ajax回调。

4。

5。

​参见递延,诺言和承诺和承诺和承诺和承诺有什么区别JavaScript中的未来?

https://www.quora.com/whats-the-difference-between-a-promise-and-a-callback-in-javascript

In all these scenarios outerScopeVar is modified or assigned a value asynchronously or happening in a later time(waiting or listening for some event to occur),for which the current execution will not wait.So all these cases current execution flow results in outerScopeVar = undefined

Let's discuss each examples(I marked the portion which is called asynchronously or delayed for some events to occur):

1.

enter image description here

Here we register an eventlistner which will be executed upon that particular event.Here loading of image.Then the current execution continuous with next lines img.src = 'lolcat.png'; and alert(outerScopeVar); meanwhile the event may not occur. i.e, funtion img.onload wait for the referred image to load, asynchrously. This will happen all the folowing example- the event may differ.

2.

2

Here the timeout event plays the role, which will invoke the handler after the specified time. Here it is 0, but still it registers an asynchronous event it will be added to the last position of the Event Queue for execution, which makes the guaranteed delay.

3.

enter image description here
This time ajax callback.

4.

enter image description here

Node can be consider as a king of asynchronous coding.Here the marked function is registered as a callback handler which will be executed after reading the specified file.

5.

enter image description here

Obvious promise (something will be done in future) is asynchronous. see What are the differences between Deferred, Promise and Future in JavaScript?

https://www.quora.com/Whats-the-difference-between-a-promise-and-a-callback-in-Javascript

绝不放开 2025-02-18 15:00:17

简短的答案是:异步性。

为什么需要异步?

javaScript是单线程,这意味着脚本的两个位不能同时运行;他们必须一个接一个地运行。在浏览器中,JavaScript共享一个线程,其中包括其他许多内容,这些内容因浏览器而异。但是通常,JavaScript与绘画,更新样式和处理用户操作(例如突出显示文本并与表单控件进行交互)处于同一队列。其中之一的活动延迟了其他事情。

您可能会使用事件和回调来解决这个问题。这是事件:

var img1 = document.querySelector('.img-1');

img1.addEventListener('load', function() {
  // image loaded
  console.log("Loaded");
});

img1.addEventListener('error', function() {
  // error caught
  console.log("Error printed");
});
<img class="img-1" src="#" alt="img">

这根本不是打喷嚏。我们得到图像,添加几个侦听器,然后JavaScript可以停止执行,直到调用其中一名侦听器为止。

不幸的是,在上面的示例中,事件可能发生在我们开始听取它们之前发生的事件,因此我们需要使用图像的“完整”属性来解决该事件:

var img1 = document.querySelector('.img-1');

function loaded() {
  // image loaded
  console.log("Loaded");
}

if (img1.complete) {
  loaded();
} else {
  img1.addEventListener('load', loaded);
}

img1.addEventListener('error', function() {
  // error caught
  console.log("Error printed");
});
<img class="img-1" src="#" alt="img">

在我们有机会聆听它们之前,这不会捕获出错误的图像。不幸的是,DOM并没有给我们一种做到这一点的方法。另外,这正在加载一张图像。如果我们想知道何时加载一组图像,事情就会变得更加复杂。

事件并不总是最好的方法

事件非常适合在同一对象上多次发生的事情 - touchstart 等。那些事件,您并不真正在乎附带听众之前发生的事情。

正确执行此操作的两种主要方法:是回调和承诺。

回调

回调是在其他函数的参数中传递的函数,此过程在JavaScript中是有效的,因为功能是对象,并且可以将对象作为参数传递给函数。回调函数的基本结构看起来像这样:

function getMessage(callback) {
  callback();
}

function showMessage() {
  console.log("Hello world! I am a callback");
}
getMessage(showMessage);

Promise

尽管有一些方法可以将回调地狱与Vanilla JS保持Bay,但承诺越来越受欢迎,目前正在ES6 (请参阅Promise)

承诺是代表异步操作的最终结果(值)的占位符,

  • 承诺占位符将被结果值(如果成功)或失败的原因(如果失败不成功)

如果您不需要知道什么时候发生了什么,而只是发生了,那么诺言就是您要寻找的

。那:

  • 诺言只能在
  • 承诺无法成功的情况下成功或失败,反之一,
  • 一旦您获得了结果,承诺是不可能的,
  • 如果承诺成功或失败了,后来您添加了成功/失败/失败回调,正确的回调将被称为
  • 事件发生在您添加回调注:始终从承诺中的功能返回结果之前发生的事件并不重要

,否则随后的功能没有任何作用。<<<。 /strong>

承诺术语

诺言可以:

  • 实现:与诺言有关的行动成功
    • 异步操作已完成
    • 承诺有一个价值
    • 承诺不会再次改变
  • 拒绝:与承诺失败的诉讼
    • 异步操作失败
    • 承诺永远不会实现
    • 承诺有一个原因,表明操作为什么失败
    • 承诺不会再次改变
  • 待定:尚未实现或拒绝
    • 异步操作尚未完成
    • 可以过渡到实现或拒绝
  • 解决:已实现或拒绝,因此是不变的

如何建立诺言

function getMessage() {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      resolve('Hello world! I am a promise');
    }, 0);
  });
}

getMessage().then(function(message) {
  console.log(message);
});

The short answer is : asynchronicity.

Why asynchronous is needed?

JavaScript is single-threaded, meaning that two bits of the script cannot run at the same time; they have to run one after another. In browsers, JavaScript shares a thread with a load of other stuff that differs from browser to browser. But typically JavaScript is in the same queue as painting, updating styles, and handling user actions (such as highlighting text and interacting with form controls). Activity in one of these things delays the others.

You've probably used events and callbacks to get around this. Here are events:

var img1 = document.querySelector('.img-1');

img1.addEventListener('load', function() {
  // image loaded
  console.log("Loaded");
});

img1.addEventListener('error', function() {
  // error caught
  console.log("Error printed");
});
<img class="img-1" src="#" alt="img">

This isn't sneezy at all. We get the image, add a couple of listeners, then JavaScript can stop executing until one of those listeners is called.

Unfortunately, in the example above, it's possible that the events happened before we started listening for them, so we need to work around that using the "complete" property of images:

var img1 = document.querySelector('.img-1');

function loaded() {
  // image loaded
  console.log("Loaded");
}

if (img1.complete) {
  loaded();
} else {
  img1.addEventListener('load', loaded);
}

img1.addEventListener('error', function() {
  // error caught
  console.log("Error printed");
});
<img class="img-1" src="#" alt="img">

This doesn't catch images that errored before we got a chance to listen for them; unfortunately, the DOM doesn't give us a way to do that. Also, this is loading one image. Things get even more complex if we want to know when a set of images have loaded.

Events aren't always the best way

Events are great for things that can happen multiple times on the same object— keyup, touchstart etc. With those events, you don't really care about what happened before you attached the listener.

The two main ways to do it correctly: are callbacks and promises.

Callbacks

Callbacks are functions that are passed inside the arguments of other functions, this procedure is valid in JavaScript because functions are objects and objects can be passed as arguments to functions. The basic structure of the callback function looks something like this:

function getMessage(callback) {
  callback();
}

function showMessage() {
  console.log("Hello world! I am a callback");
}
getMessage(showMessage);

Promise

Although there are ways to keep the callback hell at bay with vanilla JS, promises are growing in popularity and are currently being standardized in ES6 (see Promise).

A promise is a placeholder representing the eventual result (value) of an asynchronous operation

  • the promise placeholder will be replaced by the result value (if successful) or reason for failure (if unsuccessful)

If you don't need to know when something happened, but just whether it happened or not, then a promise is what you are looking for.

A promise is a bit like an event listener, except that:

  • a promise can only succeed or fail once
  • a promise can't switch from fail to success, or vice versa
  • once you have a result, the promise is immutable
  • if a promise has succeeded or failed, and you later add a success/failure callback, the correct callback will be called
  • it doesn't matter that the event occurred before you added the callback

Note: Always return a result from a function inside a Promise, otherwise there's nothing for the subsequent function to act on.

Promise Terminology

A promise can be:

  • fulfilled: The action relating to the promise succeeded
    • the asynchronous operation has completed
    • the promise has a value
    • the promise will not change again
  • rejected: The action relating to the promise failed
    • the asynchronous operation failed
    • the promise will never be fulfilled
    • the promise has a reason indicating why the operation failed
    • the promise will not change again
  • pending: Hasn't fulfilled or rejected yet
    • the asynchronous operation hasn't been completed yet
    • can transition to fulfilled or rejected
  • settled: Has been fulfilled or rejected and is thus immutable

How to Create a Promise

function getMessage() {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      resolve('Hello world! I am a promise');
    }, 0);
  });
}

getMessage().then(function(message) {
  console.log(message);
});

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文