Promise被我写嵌套了,请问如何优化? - CNode技术社区

Promise被我写嵌套了,请问如何优化?
发布于 9 年前 作者 dgnju 10455 次浏览 来自 问答

写了个了 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 的写法感觉姿势不对啊,请同学帮忙指出,谢谢。

20 回复

首先

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。

@m31271n 谢谢你的回复。throw 其实等于主动抛异常给 catch 块,这和 代码执行过程中真正的异常混到一块了,感觉也不太好。并且这里 Promise 整体上还是嵌套的。

@dgnju 确实异常混到一起了。刚刚,又处理了一下。

另外,这样,为什么是嵌套的呢?

@leizongmin 谢谢建议,只是上 coroutine 就需要上 generator 配合。 跟现有代码变化太大了。 这里确实不适合 Promise 优雅表达吗?我总感觉自己哪里没写好,想学习下如何处理

@m31271n 我是指 Promise 一层层嵌套起来了。 throw 和我直接 return true 和 false 差别不大。 原来的写法,在调用时 getLock(‘key’, 10).then( function (res) { // 这里也可以直接拿到 true 或 false })

@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种方法:

  1. 在分支处 thow new Error(’’), 或者 return Promise.reject({}), 即抛出一个自己标记过的"异常" 最后在 catch 中统一处理 在 catch 中可以判断异常是不是自己特意标记的,从而与真正的异常分开处理; 具体请参见 @m31271n @Shonke 的回复;
  2. 使用第三方库; 例如: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); }) }) }); 码字不易

回到顶部

AltStyle によって変換されたページ (->オリジナル) /