Javascript 写承诺而不打开电话

Javascript 写承诺而不打开电话,javascript,function,promise,lodash,bluebird,Javascript,Function,Promise,Lodash,Bluebird,为了停止编写大量重复代码,我尝试不打开这些调用。我最好只想从顶层传递函数。像这样 function ensureLink(srcPath, dstPath){ dstPath = fsRedux.predictDir(srcPath, dstPath) var dstDir = path.dirname(dstPath) return fsRedux.exists(dstPath) .then(_.partialRight(fsRedux.ifFalseThrow, fals

为了停止编写大量重复代码,我尝试不打开这些调用。我最好只想从顶层传递函数。像这样

function ensureLink(srcPath, dstPath){
  dstPath = fsRedux.predictDir(srcPath, dstPath)
  var dstDir = path.dirname(dstPath)
  return fsRedux.exists(dstPath)
    .then(_.partialRight(fsRedux.ifFalseThrow, false, new Error(dstPath+" exists cannot ensure link")))
    .then(_.bind(fsExtra.mkdirsAsync, fsExtra, dstDir))
    .then(_.bind(_.bind(fsExtra.linkAsync, fsExtra, srcPath, dstPath)))
}
但是,上面的代码不起作用。下面的测试表明,您无法通过
绑定的
promisifyAll
异步函数。原因是then值被传递到这些承诺中,这会使它们充当调用中的下一个参数,对于这些函数,这意味着它们作为回调启动,这就是为什么第一个测试错误在
error:timeout of 2000ms超出时会出现。确保在此测试中调用了done()回调。
在mocha中

var chai = require("chai")
var chaiAsPromised = require("chai-as-promised")
chai.use(chaiAsPromised)
chai.should()

var path = require("path")
var _ = require("lodash")
var mock = require("mock-fs")

var Promise = require("bluebird")
var fsRedux = require("./fs-redux")
var fsExtra = Promise.promisifyAll(require("fs-extra"))
var fs = Promise.promisifyAll(require("fs"))

mock({
  'path/hello-alpha.txt': 'file content here',
  'path/hello-beta.txt': 'file content here'
})

var dstPath = "path/to/fake/dir/"

function closedThen(srcPath, dstPath){
  dstPath = fsRedux.predictDir(srcPath, dstPath)
  var dstDir = path.dirname(dstPath)
  return fsRedux.exists(dstPath)
    .then(_.partialRight(fsRedux.ifFalseThrow, false, new Error(dstPath+" exists cannot ensure link")))
    .then(_.bind(fsExtra.mkdirsAsync, fsExtra, dstDir))
    .then(_.bind(_.bind(fsExtra.linkAsync, fsExtra, srcPath, dstPath)))
}

function openThen(srcPath, dstPath){
  dstPath = fsRedux.predictDir(srcPath, dstPath)
  var dstDir = path.dirname(dstPath)
  return fsRedux.exists(dstPath)
    .then(_.partialRight(fsRedux.ifFalseThrow, false, new Error(dstPath+" exists cannot ensure link")))
    .then(function(){
      return _.bind(fsExtra.mkdirsAsync, fsExtra, dstDir)()
    })
    .then(function(){
      return _.bind(fsExtra.linkAsync, fsExtra, srcPath, dstPath)()
    })
}

describe("issue", function(){
  describe("closedThen()", function(){
    it("should return then and run promise", function(){
      return closedThen("path/hello-alpha.txt", dstPath).then(function(){
        return fsExtra.readFileAsync("path/to/fake/dir/hello-alpha.txt", "utf8").should.eventually.equal("file content here")
      })
    })
  })
  describe("openThen()", function(){
    it("should return then and run promise", function(){
      return openThen("path/hello-beta.txt", dstPath).then(function(){
        return fsExtra.readFileAsync("path/to/fake/dir/hello-beta.txt", "utf8").should.eventually.equal("file content here")
      })
    })
  })
})
存在哪些函数,或者包装绑定函数的方式允许以这种方式使用承诺

更新:

我正在寻找一个函数包装套件库(lodash有一堆),它允许一个简单的接口或包装函数传递到
然后
或在
承诺内运行。reduce
。通过这种方式,将承诺链接起来真的很容易

理想情况下,我只想知道如何使该行工作,一种包装它的方法,这样当它从传递结果时,它就会忽略它。或推荐的替代方案

.then(_.bind(fsExtra.mkdirsAsync, fsExtra, dstDir))

这里有三个函数,允许您只需编写一个方法。他们使用该模块编辑承诺链外部全局范围的对象集

使用
wrap
可以忽略链中的当前值

function wrap(fn){
  return function(){
    return fn()
  }
}
使用
exportResult
可以存储该值供以后使用

function exportResult(obj, property){
  return function(value){
    dotty.put(obj, property, value)
    return value
  }
}
使用
ProviderResult
可以返回上一个结果值以返回下一个
调用

function provideResult(obj, property){
  return function(){
    return dotty.get(obj, property)
  }
}
结果是这样的

function ensureLink(srcPath, dstPath){
  dstPath = fsRedux.predictDir(srcPath, dstPath)
  var dstDir = path.dirname(dstPath)
  var values = {}
  return fsRedux.exists(dstPath)
    .then(exportResult(values, "exists"))
    .then(_.partialRight(fsRedux.ifFalseThrow, false, new Error(dstPath+" exists cannot ensure link")))
    .then(wrap(_.bind(fsExtra.mkdirsAsync, fsExtra, dstDir)))
    .then(wrap(_.bind(fsExtra.linkAsync, fsExtra, srcPath, dstPath)))
    .then(provideResult(values, "exists"))
    // continue the chain provide result from exists...
}
我最初是在寻找一个更优雅的解决方案。或对当前标准的建议。这里有一个使用的示例


真的很难理解你到底在问什么-你能进一步澄清吗。promisified函数(某种程度上根据定义)返回承诺,不再使用回调。如果调用它,它将返回一个承诺,该承诺仅在底层异步操作完成时才得到解决。此外,如果您想要串行操作,而不是并行操作,则需要将函数引用传递给
.then()
处理程序,而不是执行某个函数的返回值(除非该返回值本身就是您希望按顺序调用的函数)。唉,编辑对理解问题没有多大帮助。有一大堆不同的问题被混淆了。我至少看到一个。单元测试未正确构建,导致超时(不是失败的断言)2。误解了
bind
的工作原理。实际上,例如,最后一个
,然后
处理程序
。.bind(fsExtra.linkAsync…
接收链中先前承诺的结果。但是
fsExtra.linkAsync
将其作为第三个参数获取(在
dstPath
之后)我还是不明白。也许你可以备份并描述你想要编写脚本的特定操作序列,然后问如何编写该脚本最有效?听起来你想问一些一般性的问题,但使用一组非常复杂的代码来描述问题,而你却在问你能把这个问题简化成一个更简单的例子吗?什么是fsRedux,fsExtra.mkdirsAsync,fsExtra和其他的?@webduvet
fsRedux
是一个个人模块,命名的方法是非常自我解释的。
fsExtra
是用蓝鸟的
promisifyAll()包装的
fs extra
节点模块
。我发现拉姆达真的很有趣,但在帮助创建复杂的承诺链和管理控制流方面没有那么大帮助。你有没有看过
curryN
composeP
?你的
包装也可以使用
nAry
来完成。仔细看了拉姆达,并在上面用它发布了一个答案。我想欣赏你的想法,我希望为
ifThrow
函数提供一个替换或库。考虑到你总是将它与
一起使用。然后(R.T,R.F)
,我只会使用
。然后(R.T,R.always(Promise.reject)(新错误(…))
。或者只是蓝鸟
。catchThrow(…)
var Promise = require("bluebird")
var R = require("ramda")
var path = require("path")
var fs = Promise.promisifyAll(require("fs"))
var fse = Promise.promisifyAll(require("fs-extra"))

function ifThrow(value, desiredValue, error){
  if(value == desiredValue) throw error
  return value
}

var fsEnsureLink = function(srcpath, dstpath){
  return R.pipeP.apply(null, [
    R.always(fs.lstatAsync(srcpath).then(R.T, R.F)),
    R.partialRight(ifThrow, false, new Error("source path does not exist")),
    R.always(fs.lstatAsync(dstpath).then(R.T, R.F)),
    R.partialRight(ifThrow, true, new Error("destination path exists")),
    R.always(fse.mkdirsAsync(path.dirname(dstpath))),
    R.always(fs.linkAsync(srcpath, dstpath)),
    R.T,
  ])()
}

fsEnsureLink("./package.json", "./test/package.json")
// promise -> true || Error thrown