使用promises / callbacks等待函数在javascript

时间:2015-08-08 17:00:21

标签: javascript callback promise server-side

我有一个从三个数组中创建数据库对象的函数。数组在每个循环中填充,其中一个数组依赖于循环的相同迭代中的值。

依赖数组使用requests库和cheerio库来获取字符串以填充数组。

目前依赖数组填充空值,我认为是因为循环不等待返回请求。

我还在学习,并希望在没有直接阻止的情况下让它工作以保持异步,所以我正在研究promises / callbacks。

这是在服务器端完成的,但是从我在cheerios docs中看到的,没有承诺能力。

这是我到目前为止所拥有的。 (getFile()是不填充'c'数组的函数,它还取决于放入'b'的当前值。我知道getFile函数通过控制台日志测试获得正确的值,因此问题必须在填充'c'的实现中。

addToDB()是一个将值保存到mongoDB中的函数,从测试我知道对象被正确放入db,只是c数组不正确。

function getInfo(path) {
  $(path).each(function(i,e) {
    a.push(...)
    b.push(value)
    c.push(getFile(value))
  })
  var entry = new DB...//(a,b,c)
  addToDB(entry);
}

function getFile(...) {
  request(fullUrl, function (err, resp, page) {
    if (!err && resp.statusCode == 200) {
      var $ = cheerio.load(page); // load the page
      srcEp = $(this).attr("src");
      return srcEp;
    } // end error and status code
  }); // end request
}

我一直在阅读承诺/回调然后()但我还没有发现任何有用的东西。

1 个答案:

答案 0 :(得分:0)

首先,您必须了解这样一个事实,即至少部分依赖于异步子流程的任何流程本身就是异步的。

在此问题代码的最低级别,request()是异步的,因此其调用者getFile()是异步的,其调用者getInfo()也是异步的。< / p>

Promise是异步流程结果的抽象,有助于对这些流程完成时要采取的行动进行编码 - 成功或失败。

通常,低级别异步函数应该返回一个承诺,由其调用者执行操作,这反过来会向调用者返回一个promise,依此类推调用堆栈。在每个函数内部,可以使用promise方法(主要是.then())对返回的promise做出操作,并且可以使用Promise.all()进行聚合(例如语法不同)。

在这个问题中,没有证据表明request()当前返回了一个承诺。您有三种选择:

  • 发现request()确实会返回一个承诺。
  • 重写request()以返回承诺。
  • 编写调用request()的适配器函数(&#34; promisifier&#34;),并生成/返回承诺,稍后根据request()的结果履行或拒绝承诺。

第一个或第二个选项是理想的,但我(Roamer)的安全假设是假设需要一个适配器。幸运的是,我从问题中知道足够能够写一个。 Cheerio似乎不包含jQuery的promise实现,因此需要一个专用的promise lib。

这是一个适配器函数,使用的语法将与Bluebird lib或本机js promises一起使用:

//Promisifier for the low level function request()
function requestAsync(url) {
    return new Promise(function(resolve, reject) {
        request(url, function(err, resp, page) {
            if (err) {
                reject(err);
            } else {
                if (resp.statusCode !== 200) {
                    reject(new Error('request error: ' + resp.statusCode));
                }
            } else {
                resolve(page);
            }
        });
    });
}

现在可以编写getFile(...)getInfo()来使用从最低级别的适配器返回的承诺。

//intermediate level function
function getFile(DOMelement) {
    var fullUrl = ...;//something derived from DOMelement. Presumably either .val() or .text()
    return requestAsync(fullUrl).then(function (page) {
        var $ = cheerio.load(page);
        srcEp = $(???).attr('src');//Not too sure what the selector should be. `$(this)` definitely won't work.
        return srcEp;
    });
}

//high level function
function getInfo(path) {
    var a = [], b = [], c = [];//presumably

    // Now map the $(path) to an array of promises by calling getFile() inside a .map() callback.
    // By chaining .then() a/b/c are populated when the async data arrives. 
    var promises = $(path).map(function(i, e) {
        return getFile(e).then(function(srcEp) {
            a[i] = ...;
            b[i] = e;
            c[i] = srcEp;
        });
    });
    //return an aggregated promise to getInfo's caller,
    //in case it needs to take any action on settlement.
    return Promise.all(promises).then(function() {
        //What to do when all promises are fulfilled
        var entry = new DB...//(a,b,c)
        addToDB(entry);
    }, function(error) {
        //What to do if any of the promises fails
        console.log(error);
        //... you may want to do more.
    });
}