如何在javascript中同步调用一组函数

Posted

技术标签:

【中文标题】如何在javascript中同步调用一组函数【英文标题】:How to synchronously call a set of functions in javascript 【发布时间】:2016-10-09 01:24:24 【问题描述】:

我正在开发一个需要获取一些数据并对其进行处理的 javascript 项目,但是我遇到了 JavaScript 的异步特性问题。我希望能够做的事情如下。

//The set of functions that I want to call in order
function getData() 
    //gets the data


function parseData() 
    //does some stuff with the data


function validate() 
    //validates the data


//The function that orchestrates these calls 
function runner() 
    getData();
    parseData();
    validate();

在这里,我希望每个函数在继续下一次调用之前等待完成,因为我遇到了程序在检索数据之前尝试验证数据的情况。但是,我也希望能够从这些函数返回一个值进行测试,所以我不能让这些函数返回一个布尔值来检查完成情况。在继续下一个调用之前,如何让 javascript 等待函数运行完成?

【问题讨论】:

"...我对 JavaScript 的异步特性感到困惑..." JavaScript 本身基本上没有异步特性。它经常用在 环境 中,例如浏览器或 NodeJS。 我建议阅读Promises。 Promisify 所有三个然后使用 Promise.all 等待所有三个。可以通过 Promise 实现的基本内容。 你所需要的只是回调函数,或者 Promise [javascript] asynchronous functions series 【参考方案1】:

使用承诺:

//The set of functions that I want to call in order
function getData(initialData) 
  //gets the data
  return new Promise(function (resolve, reject) 
    resolve('Hello World!')
  )


function parseData(dataFromGetDataFunction) 
  //does some stuff with the data
  return new Promise(function (resolve, reject) 
    resolve('Hello World!')
  )


function validate(dataFromParseDataFunction) 
  //validates the data
  return new Promise(function (resolve, reject) 
    resolve('Hello World!')
  )


//The function that orchestrates these calls 
function runner(initialData) 
    return getData(initialData)
        .then(parseData)
        .then(validate)


runner('Hello World!').then(function (dataFromValidateFunction) 
    console.log(dataFromValidateFunction);
)

它们不仅易于掌握,而且从代码可读性的角度来看也是完全合理的。阅读更多关于他们的信息here。如果是浏览器环境,推荐thispolyfill。

【讨论】:

当我让每一个使用承诺时,我是否可以让它们也返回类似字符串的东西,或者承诺必须是函数返回的唯一东西?我希望能够使用该字符串来检查输出与预期输出。 这取决于您的用例。如果您想链接它们(就像您在帖子中建议的那样),您需要返回一个承诺,因为需要有一个“then”方法来调用下一个函数。但是,如果您只关心知道所有三个函数何时完成,您可以返回一个承诺或其他值,并使用名为“Promise.all”的方法将它们全部捕获,但它们不会遵循顺序。跨度> 他们必须遵循一个顺序,但我也需要能够从中获得价值。另外,承诺是内置在 javascript 中的,还是我需要从某个地方得到它? 我想我可以将一个变量设置为我需要的值,但这似乎不太理想,不是吗? 您将能够获取这些值,我刚刚编辑了帖子以演示这一点,查看函数上的参数以了解数据如何流动。大多数modern browsers 都内置了Promise,对于遗留支持,请使用我在帖子中建议的“polyfill”。【参考方案2】:

您引用的代码将同步运行。 JavaScript 函数调用是同步的。

所以我假设getDataparseData 和/或validate 涉及异步 操作(例如在浏览器中使用ajax,或在NodeJS 中使用readFile )。如果是这样,您基本上有两种选择,这两种选择都涉及回调

首先是让这些函数接受它们在完成时将调用的回调,例如:

function getData(callback) 
    someAsyncOperation(function() 
        // Async is done now, call the callback with the data
        callback(/*...some data...*/);
    );

你会这样使用它:

getData(function(data) 
    // Got the data, do the next thing
);

回调的问题在于它们很难组合并且具有相当脆弱的语义。因此发明了 promises 以赋予它们更好的语义。在 ES2015(又名“ES6”)或具有不错的 Promise 库中,它看起来像这样:

function getData(callback) 
    return someAsyncOperation();

或者如果someAsyncOperation 没有启用承诺,那么:

function getData(callback) 
    return new Promise(function(resolve, reject) 
        someAsyncOperation(function() 
            // Async is done now, call the callback with the data
            resolve(/*...some data...*/);
            // Or if it failed, call `reject` instead
        );
    );

似乎对你没有多大作用,但关键之一是可组合性;你的最终函数最终看起来像这样:

function runner() 
    return getData()
        .then(parseData) // Yes, there really aren't () on parseData...
        .then(validate); // ...or validate

用法:

runner()
    .then(function(result) 
         // It worked, use the result
    )
    .catch(function(error) 
         // It failed
    );

这是一个例子;它只能在支持Promise 和 ES2015 箭头函数的相当新的浏览器上运行,因为我很懒,用箭头函数编写它并且没有包含 Promise 库:

"use strict";

function getData() 
    // Return a promise
    return new Promise((resolve, reject) => 
        setTimeout(() => 
            // Let's fail a third of the time
            if (Math.random() < 0.33) 
                reject("getData failed");
             else 
                resolve('"msg":"This is the message"');
            
        , Math.random() * 100);
    );


function parseData(data) 
    // Note that this function is synchronous
    return JSON.parse(data);


function validate(data) 
    // Let's assume validation is synchronous too
    // Let's also assume it fails half the time
    if (!data || !data.msg || Math.random() < 0.5) 
        throw new Error("validation failed");
    
    // It's fine
    return data;


function runner() 
    return getData()
        .then(parseData)
        .then(validate);


document.getElementById("the-button").addEventListener(
    "click",
    function() 
        runner()
            .then(data => 
                console.log("All good! msg: " + data.msg);
            )
            .catch(error => 
                console.error("Failed: ", error && error.message || error);
            );
    ,
  false
);
<input type="button" id="the-button" value="Click to test">
(you can test more than once)

【讨论】:

【参考方案3】:

您应该更改每个函数以返回一个Promise,这将使您的最终函数变为:

function runner() 
    return Promise.try(getData).then(parseData).then(validate);

为此,每个函数的主体都应该包装在一个新的 Promise 中,例如:

function getData() 
  return new Promise(function (res, rej) 
    var req = new AjaxRequest(...); // make the request
    req.onSuccess = function (data) 
      res(data);
    ;
  );

这是一个关于 Promise 可能如何工作的非常粗略的示例。如需更多阅读,请查看:

2ality 的精彩博文:part 1 和 part 2 bluebird 在why promises 上的文档 mdn 在JS' Promise class 上的文档

【讨论】:

所以promise不是一个库或者我需要参考的东西?它是内置在javascript中的吗? Promise 是一个标准化的 API,在 JS 中内置了一个实现(我的答案中的 MDN 链接)。它支持 in most browsers 但不支持 IE,因此 Bluebird 提供了一个库版本,其中包含一些额外的功能(以及更好的性能)。 真棒,它是一个内置的东西。感谢您的澄清。

以上是关于如何在javascript中同步调用一组函数的主要内容,如果未能解决你的问题,请参考以下文章

如何在同步模式下调用javascript函数

如何使用 QT/python 从 Javascript 调用 C++ 函数?

调用condition_variable等待函数时线程如何等待?

同步调用异步 Javascript 函数

如何实现 javascript “同步”调用 app 代码

如何在 javascript 中同步异步映射函数