我如何从函数 foo 返回一个无同步请求的答案/结果?

我正在尝试从呼叫返回的值,以及将结果分配到函数内部的本地变量,并返回其中一个,但没有这些方式实际上返回答案 - 他们都返回不确定的或无论变量结果的初始值是什么。

一个不同步函数的例子,接受召回(使用 jQuery 的 ajax 函数):

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`
}

使用 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`
}

例如,使用那时承诺的区块:

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`
}

当前回答

当然,有很多方法,如同步请求,承诺,但从我的经验,我认为你应该使用呼叫回来方法。

因此,您的代码剪辑可以重新写成有点不同:

function foo() {
    var result;

    $.ajax({
        url: '...',
        success: function(response) {
            myCallback(response);
        }
    });

    return result;
}

function myCallback(response) {
    // Does something.
}

其他回答

另一种方法是从无同步函数中返回值,是通过将结果从无同步函数中存储的对象。

下面是相同的例子:

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

我正在使用结果对象来存储在无同步操作期间的值,这使得结果即使在无同步工作后也可用。

我使用这个方法很多,我会感兴趣知道这个方法有多好运作,在那里通过连续模块将结果传输回来。

使用 ES2017 您应该将此作为功能声明。

async function foo() {
  var response = await $.ajax({url: '...'})
  return response;
}

这样执行它。

(async function() {
  try {
    var result = await foo()
    console.log(result)
  } catch (e) {}
})()

或承诺的合成。

foo().then(response => {
  console.log(response)

}).catch(error => {
  console.log(error)

})

Stack Snippet 显示上面的代码。

// 函数声明: async 函数 foo() { var 回复 = 等待 $.ajax({ url: 'https://jsonplaceholder.typicode.com/todos/1' }) 返回回回复; } // 执行此类: (async 函数() { 尝试 { var 结果 = 等待 foo() console.log(result) } 捕捉(e) {} }() // 或使用 承诺 合成: foo().then(回复 => { console.log(response) }。

function findItem() {
    var item;
    while(item_not_found) {
        // search
    }
    return item;
}

var item = findItem();

// Do something with item
doSomethingElse();

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



重要: 您只能在一个 async 函数或 JavaScript 模块中使用等待。 顶级等待不支持模块之外,因此您可能需要创建一个 async IIFE (即时邀请函数表达) 以启动一个 async 背景,如果不使用模块。

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


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

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

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


更多关于承诺的信息: HTML5 Rocks - JavaScript 承诺。

副注:jQuery的废弃物品

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
}

但修正很容易:

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

jQuery

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

如果您使用任何其他 jQuery Ajax 方法,如 $.get, $.getJSON 等,您必须将其更改为 $.ajax (因为您只能将配置参数转移到 $.ajax)。

短答:您的 foo() 方法即时返回,而 $ajax() 通话在函数返回后无同步执行。

也许最简单的方式是将对象转移到 foo() 方法,并在 async 呼叫完成后将结果存储在该对象的一个成员中。

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

请注意,对 foo() 的呼叫仍然不会有用,但是,对 async 呼叫的结果现在将存储在 result.response 中。

这是其中一个地方,在许多新的JavaScript框架中使用的双向数据连接或存储概念将为您工作。

因此,如果您使用 Angular, React 或任何其他框架,使双向数据连接或存储概念,这个问题只是为您固定,所以简单地说,您的结果在第一阶段是不确定的,所以您在收到数据之前得到了结果 = 不确定的,然后一旦您获得结果,它将被更新并被分配到新的值,你的 Ajax 呼叫的反应。

但是,你如何在纯粹的JavaScript或jQuery中这样做,例如,你在这个问题上问的那样?

例如,在承诺中,我们有某些功能,如成功() 或之后() 将执行,当您的数据为您准备好。

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