在不打开的情况下编写承诺然后调用
Writing promises without opening then calls
为了停止编写大量重复代码,我尝试不打开 then 调用。我最好只喜欢从顶层传递函数。像这样。
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)))
}
但是上面的代码不起作用。下面的测试表明您无法传递 bound
promisifyAll
异步函数。原因是 then 值被传递到这些 promise 中,这导致它们充当调用中的下一个参数,对于这些函数,这意味着它们作为回调触发,这就是为什么第一个测试错误出现 Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.
在摩卡中。
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")
})
})
})
})
存在哪些函数,或者包装绑定函数的方式允许以这种方式处理 promises?
更新:
我正在寻找一个函数包装器套件库(lodash 有很多),它允许 partialing or 的简单接口或包装函数以传递给 then
或运行 内 Promise.reduce
。这样链接 promise 真的很容易。
理想情况下,我只想知道如何使该行工作,一种包装它的方法,以便当它从 then 传递结果时忽略它。或者推荐的替代方案。
.then(_.bind(fsExtra.mkdirsAsync, fsExtra, dstDir))
这里有三个函数,让您只需编写单一方法深度承诺。他们使用模块 dotty
来编辑设置为承诺链之外的全局范围的对象。
使用 wrap
你可以忽略链中当前的 then 值。
function wrap(fn){
return function(){
return fn()
}
}
使用 exportResult
您可以存储该值供以后使用。
function exportResult(obj, property){
return function(value){
dotty.put(obj, property, value)
return value
}
}
使用 provideResult
您可以 return 之前的结果值 return 到下一个 then
调用。
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...
}
我原本是在寻找一个更优雅的解决方案。或者关于执行此操作的当前标准的建议。
这是一个使用 Ramda
的例子。
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
为了停止编写大量重复代码,我尝试不打开 then 调用。我最好只喜欢从顶层传递函数。像这样。
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)))
}
但是上面的代码不起作用。下面的测试表明您无法传递 bound
promisifyAll
异步函数。原因是 then 值被传递到这些 promise 中,这导致它们充当调用中的下一个参数,对于这些函数,这意味着它们作为回调触发,这就是为什么第一个测试错误出现 Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.
在摩卡中。
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")
})
})
})
})
存在哪些函数,或者包装绑定函数的方式允许以这种方式处理 promises?
更新:
我正在寻找一个函数包装器套件库(lodash 有很多),它允许 partialing or then
或运行 内 Promise.reduce
。这样链接 promise 真的很容易。
理想情况下,我只想知道如何使该行工作,一种包装它的方法,以便当它从 then 传递结果时忽略它。或者推荐的替代方案。
.then(_.bind(fsExtra.mkdirsAsync, fsExtra, dstDir))
这里有三个函数,让您只需编写单一方法深度承诺。他们使用模块 dotty
来编辑设置为承诺链之外的全局范围的对象。
使用 wrap
你可以忽略链中当前的 then 值。
function wrap(fn){
return function(){
return fn()
}
}
使用 exportResult
您可以存储该值供以后使用。
function exportResult(obj, property){
return function(value){
dotty.put(obj, property, value)
return value
}
}
使用 provideResult
您可以 return 之前的结果值 return 到下一个 then
调用。
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...
}
我原本是在寻找一个更优雅的解决方案。或者关于执行此操作的当前标准的建议。
这是一个使用 Ramda
的例子。
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