写了个了 redis 加锁的函数,写完发现 promise 嵌套了,感觉有点奇怪。
getLock: function (lockName, seconds) {
var self = this;
lockName = self.REDIS_LOCK_PREFIX + lockName;
var expireTimeInMs = Date.now() + seconds * 1000 + 1;
return self.setnx(lockName, expireTimeInMs).then(function (res) {
if (res === 1) {
return true;
} else {
return self.get(lockName).then(function (previousExpireInMs) {
// 过期死锁
if (previousExpireInMs < Date.now()) {
return self.getset(lockName,expireTimeInMs).then(function (getSetReturnExpireInMs) {
// 重新设置成功
if(previousExpireInMs===getSetReturnExpireInMs){
return true
}else{
return false
}
}).catch(function () {
return false;
})
}else{
return false
}
}).catch(function (err) {
return false;
})
}
}).catch(function (err) {
logger.error(``);
})
},
假设 setnx,get,getset 都是同步函数,那么我想实现的逻辑如下:
function getLock() {
var res = setnx(lockName, expireTimeInMs);
if (res === 1) {
return true;
} else {
var previousExpireInMs = get(lockName);
if (previousExpireInMs < Date.now()) {
var getSetReturnExpireInMs = getset(lockName, expireTimeInMs);
if (previousExpireInMs === getSetReturnExpireInMs) {
return true
} else {
return false
}
} else {
return false
}
}
}
我 promise 的写法感觉姿势不对啊,请同学帮忙指出,谢谢。
首先
var a = Promise.resolve(1)
var b = a.then((a)=>a)
b 并不是1,你 return 有何意图 。
其次
if (res === 1) {
return true;
}
.....
else 可以去掉
你可以通过这段代码理解。
function(err, data){
if(err)console.log(err);
.....
}
其他的没玩过。
function getDataPromise(){
return Promise.resolve({ data:{name:'yugo',age:'30',gender:'mail'} })
}
function getData(){
return getDataPromise().then((done) => {
return Object.assign({},done.data,{ name:'miyogurt' })
})
}
getData().then((data)=>{
console.log(data)
})
getDataPromise().then((data)=>{
console.log(data.data)
return data.data.name;
}).then((name) => {
console.log("name: " + name)
})
你看下,这个是不是你想要的。
「Promise 对象的 then(onfulfilled)
方法」返回「一个以 onfulfilled
函数的返回值进行 resolve 的 Promise 对象」。记住这点,你就可以把嵌套的 Promise 扁平化了。
then…return…then…return
@MiYogurt getLock 要返回 Promise
@m31271n 假如3个嵌套的 Promise 每一个then 的返回都作为下一个的输入,3个都会执行的情况,很好拉平。到我这例子可能执行完某一个后面就不需要执行了,要能跳出来,困难在这
@alsotang 唐少,请问如果逻辑不需要一路then 到底,中途需要跳出来的情况怎么写呢? ( 如果某一环 reject 跳到 catch 里也觉得很怪)
这Promise写得还不如callback来得简单直接。
@leizongmin 主要是这个对象是提供API的,里面十几个函数都是返回 Promise, 想统一啊
getLock: function (lockName, seconds) {
lockName = self.REDIS_LOCK_PREFIX + lockName;
var expireTimeInMs = Date.now() + seconds * 1000 + 1;
return this.setnx(lockName, expireTimeInMs).then(res => {
if (res === 1) throw true
return self.get(lockName)
}).then(previousExpireInMs => {
// 过期死锁
if (previousExpireInMs >= Date.now()) throw false
return this.getset(lockName,expireTimeInMs)
}).then(getSetReturnExpireInMs => {
// 重新设置成功
if (previousExpireInMs===getSetReturnExpireInMs) throw true
throw false
}).catch(err => {
if (err === true || err === false) {
return err
} else {
throw err
}
})
}
@dgnju 我觉得可以像上面这样写: ES6 原生的 Promise 不提供 Promise.break 这种方法。但是可以使用 throw 来终止 Promise 链的继续执行。 最后使用 catch 捕获这些 throw 出的 true/false。
你需要一个 coroutine 库,比如 https://github.com/leizongmin/lei-coroutine
@m31271n 谢谢你的回复。throw 其实等于主动抛异常给 catch 块,这和 代码执行过程中真正的异常混到一块了,感觉也不太好。并且这里 Promise 整体上还是嵌套的。
@dgnju 确实异常混到一起了。刚刚,又处理了一下。
另外,这样,为什么是嵌套的呢?
@leizongmin 谢谢建议,只是上 coroutine 就需要上 generator 配合。 跟现有代码变化太大了。 这里确实不适合 Promise 优雅表达吗?我总感觉自己哪里没写好,想学习下如何处理
@m31271n 我是指 Promise 一层层嵌套起来了。 throw 和我直接 return true 和 false 差别不大。 原来的写法,在调用时 getLock(‘key’, 10).then( function (res) { // 这里也可以直接拿到 true 或 false })
@dgnju 用 yield
@dgnju 目前 Promise 对分支支持不太友好,用 reject 然后 .then(null,=>console.info()) 捕获怎么样? 用cacth没啥不好的啊,迭代器不也是用的异常实现的嘛?
用co改造后是不是更好理解呢
getLock: function (lockName, seconds) {
var self = this;
lockName = self.REDIS_LOCK_PREFIX + lockName;
var expireTimeInMs = Date.now() + seconds * 1000 + 1;
return co(function* (){
let res = yield self.setnx(lockName, expireTimeInMs);
if (res === 1) {
return true;
}
let previousExpireInMs = yield self.get(lockName);
// 过期死锁
if (previousExpireInMs < Date.now()) {
let getSetReturnExpireInMs = yield self.getset(lockName,expireTimeInMs);
// 重新设置成功
if(previousExpireInMs===getSetReturnExpireInMs){
return true
}
}
return false;
}).catch(err=>{
logger.error(err);
return false;
});
}
首先谢谢楼上各位的回复。 总结下: 如果需要提前跳出 promise 链,那么主要有2种方法:
- 在分支处 thow new Error(’’), 或者 return Promise.reject({}), 即抛出一个自己标记过的“异常” 最后在 catch 中统一处理 在 catch 中可以判断异常是不是自己特意标记的,从而与真正的异常分开处理; 具体请参见 @m31271n @Shonke 的回复;
- 使用第三方库; 例如:bluebird 支持 cancel 方法; 请参考: http://bluebirdjs.com/docs/api/cancellation.html http://stackoverflow.com/questions/28572180/can-i-break-a-chain-early-with-bluebird-promises https://cnodejs.org/topic/58385d4927d001d606ac197d
如果可以引入 generator 的话,那么配合可以使用 co,或者 Promise.coroutine 等改善; 请参见楼上:@leizongmin @alsotang @chrislbb 等童鞋的回复
提供一种promise的写法, new Promise((res,rej)=>{ res(resObj);//or rej(rejObj); }) .then((data)=>{ return promise_task; }) .then(…) .catch(err=>{ }) promise_task写成这样的: fs.readFile(path,(err,doc)=>{ if(err)throw err; return doc; }) promise_task或者封装成promise化的函数 new Promise((res,rej)=>{ fs.readFile(path,(err,doc)=>{ if(err)rej(‘some error occur’); res(doc); }) }) }); 码字不易