我如何从函数 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`
}

当前回答

var milk = order_milk();
put_in_coffee(milk);

这个问题的经典JavaScript方法,利用JavaScript支持作为可以通过的第一类对象的功能,是将一个功能作为一个参数转移到无同步的请求,它将随后引用,当它在未来完成任务时。

order_milk(put_in_coffee);

order_milk kicks off, orders the milk, then, when and only when it arrives, it invokes put_in_coffee。

order_milk(function(milk) { put_in_coffee(milk, drink_coffee); }

我正在转到把牛奶放进去,以及当牛奶放进去后执行的行动(喝咖啡)。

var answer;
$.ajax('/foo.json') . done(function(response) {
  callback(response.data);
});

function callback(data) {
  console.log(data);
}

加入承诺

order_milk() . then(put_in_coffee)

order_milk() . then(put_in_coffee) . then(drink_coffee)

function get_data() {
  return $.ajax('/foo.json');
}

get_data() . then(do_something)

get_data() .
  then(function(data) { console.log(data); });

get_data() .
  then(data => console.log(data));

a();
b();

a() . then(b);

async function morning_routine() {
  var milk   = await order_milk();
  var coffee = await put_in_coffee(milk);
  await drink(coffee);
}

async function foo() {
  data = await get_data();
  console.log(data);
}

其他回答

// WRONG
var results = [];
theArray.forEach(function(entry) {
    doSomethingAsync(entry, function(result) {
        results.push(result);
    });
});
console.log(results); // E.g., using them, returning them, etc.

平行

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

例子:

请注意我们如何使用 forEach 的指数,以保存结果的结果与其所涉及的输入相同的位置,即使结果从顺序中出来(因为 async 通话不一定在它们开始的顺序中完成)。

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

例子:

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

或者这里是返回承诺的版本:

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

当然,如果 doSomethingAsync 传递给我们错误,我们将使用拒绝来拒绝承诺,当我们得到错误时。

function doSomethingWith(theArray) {
    return Promise.all(theArray.map(function(entry) {
        return doSomethingAsync(entry);
    }));
}
doSomethingWith(theArray).then(function(results) {
    console.log("Results:", results);
});

function doSomethingWith(theArray) {
    return Promise.all(theArray.map(doSomethingAsync));
}
doSomethingWith(theArray).then(function(results) {
    console.log("Results:", results);
});

系列

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

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

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

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

例子:

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

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

function foo() {
    var result;

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

    return result;
}

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

下面是一些方法来处理无同步请求:

浏览器承诺对象 Q - 一个承诺图书馆为JavaScript A+ Promises.js jQuery 提交 XMLHttpRequest API 使用呼叫回复概念 - 作为实施在第一个答案

示例: jQuery 转移实施,以处理多个请求

應用程式 應用程式 應用程式 應用程式 應用程式 應用程式 應用程式 應用程式 應用程式 應用程式 應用程式 應用程式 應用程式 應用程式

var milk = order_milk();
put_in_coffee(milk);

这个问题的经典JavaScript方法,利用JavaScript支持作为可以通过的第一类对象的功能,是将一个功能作为一个参数转移到无同步的请求,它将随后引用,当它在未来完成任务时。

order_milk(put_in_coffee);

order_milk kicks off, orders the milk, then, when and only when it arrives, it invokes put_in_coffee。

order_milk(function(milk) { put_in_coffee(milk, drink_coffee); }

我正在转到把牛奶放进去,以及当牛奶放进去后执行的行动(喝咖啡)。

var answer;
$.ajax('/foo.json') . done(function(response) {
  callback(response.data);
});

function callback(data) {
  console.log(data);
}

加入承诺

order_milk() . then(put_in_coffee)

order_milk() . then(put_in_coffee) . then(drink_coffee)

function get_data() {
  return $.ajax('/foo.json');
}

get_data() . then(do_something)

get_data() .
  then(function(data) { console.log(data); });

get_data() .
  then(data => console.log(data));

a();
b();

a() . then(b);

async function morning_routine() {
  var milk   = await order_milk();
  var coffee = await put_in_coffee(milk);
  await drink(coffee);
}

async function foo() {
  data = await get_data();
  console.log(data);
}

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