栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

为什么现在没有我的未来价值?

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

为什么现在没有我的未来价值?

→有关使用不同示例的异步行为的更一般说明,请参见
在函数内部修改变量后
为什么变量未更改?-异步代码参考

→如果您已经理解问题,请跳至下面的可能解决方案。

问题

中的Ajax代表
异步
。这意味着发送请求(或接收响应)已从正常执行流程中删除。在您的示例中,

$.ajax
立即返回并在调用
returnresult;
作为
success
回调传递的函数之前执行下一条语句。

这是一个类比,希望可以使同步流和异步流之间的区别更加清晰:

同步

假设您打了一个电话给朋友,并请他为您找东西。尽管可能要花一些时间,但您还是要等电话并凝视太空,直到您的朋友给您所需的答案。

当您进行包含“普通”代码的函数调用时,也会发生相同的情况:

function findItem() {    var item;    while(item_not_found) {        // search    }    return item;}var item = findItem();// Do something with itemdoSomethingElse();

即使

findItem
执行可能花费很长时间,但之后的任何代码
var item = findItem();
也必须 等到 函数返回结果为止。

异步

您出于相同的原因再次给您的朋友打电话。但是这次您告诉他您很着急,他应该用您的手机 给您回电
。您挂断电话,离开房屋,然后按计划做。一旦您的朋友给您回电,您就可以处理他提供给您的信息。

这正是您执行Ajax请求时发生的事情。

findItem(function(item) {    // Do something with the item});doSomethingElse();

无需等待响应,而是立即继续执行,并执行Ajax调用后的语句。为了最终获得响应,您提供了一个在收到响应后即被调用的函数,即 回调 (注意什么? 回叫
?)。在调用之后执行的任何语句都将在调用回调之前执行。


解决方案

拥抱Javascript的异步特性! 尽管某些异步操作提供了同步对应项(“ Ajax”也是如此),但通常不鼓励使用它们,尤其是在浏览器上下文中。

你问为什么不好?

Javascript在浏览器的UI线程中运行,任何长时间运行的进程都将锁定UI,使其无响应。此外,Javascript的执行时间有上限,浏览器会询问用户是否继续执行。

所有这些都是非常糟糕的用户体验。用户将无法判断一切是否正常。此外,对于连接速度较慢的用户,效果会更糟。

在下面的内容中,我们将研究三个互为基础的不同解决方案:

  • 承诺
    async/await
    (ES2017 +,如果使用转译器或再生器,则在较旧的浏览器中可用)
  • 回调 (在节点中受欢迎)
  • 承诺
    then()
    (ES2015 +,如果您使用许多承诺库之一,则在较旧的浏览器中可用)

在当前浏览器和节点7+中,所有这三个功能均可用。


ES2017 +:承诺 [
async/await
](https://developer.mozilla.org/en-

US/docs/Web/Javascript/Reference/Statements/async_function)

2017年发布的ECMAscript版本引入了对异步功能的 语法级支持
。借助

async
await
,您可以以“同步样式”编写异步。该代码仍然是异步的,但更易于阅读/理解。

async/await
建立在promise之上:
async
函数总是返回promise。
await
“解包”一个承诺,或者导致承诺所解决的价值,或者如果该承诺被拒绝,则抛出错误。

重要提示:
您只能

await
async
函数内部使用。目前,
await
尚不支持顶层,因此您可能必须进行异步IIFE(立即调用函数表达式)才能启动
async
上下文。

你可以阅读更多关于

async
await
的MDN。

这是一个基于以上延迟的示例:

// Using 'superagent' which will return a promise.var superagent = require('superagent')// This is isn't declared as `async` because it already returns a promisefunction 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 await 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);})();

当前的浏览器和节点版本支持

async/await
。您还可以通过使用再生器(或使用再生器的工具,例如Babel)将代码转换为ES5来支持较旧的环境。


让函数接受 回调

回调只是传递给另一个函数的一个函数。该其他函数可以随时调用传递的函数。在异步过程的上下文中,只要完成异步过程,就会调用回调。通常,结果将传递给回调。

在问题的示例中,您可以

foo
接受回调并将其用作
success
回调。所以这

var result = foo();// Code that depends on 'result'

变成

foo(function(result) {    // Code that depends on 'result'});

在这里,我们定义了函数“内联”,但是您可以传递任何函数引用:

function myCallback(result) {    // Code that depends on 'result'}foo(myCallback);

foo
本身定义如下:

function foo(callback) {    $.ajax({        // ...        success: callback    });}

callback
foo
在调用它时引用我们传递给的函数,而只是将其传递给
success
。即,一旦Ajax请求成功,
$.ajax
将调用
callback
并将响应传递给回调(可以使用进行引用
result
,因为这是我们定义回调的方式)。

您还可以在将响应传递给回调之前进行处理:

function foo(callback) {    $.ajax({        // ...        success: function(response) { // For example, filter the response callback(filtered_response);        }    });}

使用回调编写代码比看起来容易。毕竟,浏览器中的Javascript很大程度上是事件驱动的(DOM事件)。接收Ajax响应不过是一个事件。
当您必须使用第三方代码时,可能会遇到困难,但是大多数问题可以通过思考应用程序流程来解决。


ES2015 +:对[then()的](https://developer.mozilla.org/en-

US/docs/Web/Javascript/Reference/Global_Objects/Promise)承诺

该承诺API是ECMAscript的6(ES2015)的新功能,但它有很好的浏览器支持了。还有许多实现标准Promises
API的库,并提供其他方法来简化异步函数(例如bluebird)的使用和组合。

承诺是 未来 价值的容器。当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).  });

应用于我们的Ajax调用,我们可以使用如下承诺:

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("/echo/json")  .then(function(result) {    // Code depending on result  })  .catch(function() {    // An error occurred  });

描述promise提供的所有优点超出了此答案的范围,但是如果您编写新代码,则应认真考虑它们。它们为您的代码提供了很好的抽象和分离。

有关诺言的更多信息:HTML5摇滚-Javascript
Promises

旁注:jQuery的延迟对象

延迟对象是jQuery的promise的自定义实现(在Promise
API标准化之前)。它们的行为几乎像promise,但是暴露了稍微不同的API。

jQuery的每个Ajax方法都已经返回了“延迟对象”(实际上是对延迟对象的承诺),您可以从函数中返回它:

function ajax() {    return $.ajax(...);}ajax().done(function(result) {    // Code depending on result}).fail(function() {    // An error occurred});

旁注:承诺陷阱

请记住,承诺和递延对象只是将来价值的 容器 ,它们不是价值本身。例如,假设您具有以下内容:

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}

此代码误解了上述异步问题。具体来说,

$.ajax()
它不会在检查服务器上的“ / password”页面时冻结代码-
它会向服务器发送请求,而在等待时,它会立即返回jQuery Ajax
Deferred对象,而不是服务器的响应。这意味着该
if
语句将始终获取此Deferred对象,将其视为
true
,然后像用户已登录一样继续进行。

但是解决方法很简单:

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});

不推荐:同步“ Ajax”调用

如前所述,某些异步操作具有同步的对应对象。我不主张使用它们,但是出于完整性考虑,这是执行同步调用的方式:

没有jQuery

如果您直接使用

XMLHttpRequest
对象,请
false
作为第三个参数传递给
.open

jQuery的

如果使用jQuery,则可以将

async
选项设置为
false
。请注意,自jQuery
1.8起 不推荐使用
此选项。然后,您仍然可以使用
success
回调或访问jqXHR对象的
responseText
属性:

function foo() {    var jqXHR = $.ajax({        //...        async: false    });    return jqXHR.responseText;}

如果使用任何其他的jQuery的Ajax的方法,例如

$.get
$.getJSON
等等,必须将其改为
$.ajax
(因为你只能传递配置参数
$.ajax
)。

小心! 不可能发出同步JSONP请求。JSONP本质上始终是异步的(还有一个甚至不考虑此选项的原因)。



转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/369238.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号