CNode

Promise被我写嵌套了,请问如何优化?

问答
Ddgnju发布于10 年前最后回复10 年前20 回复10735 浏览0 收藏

写了个了 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)

S
Shonke#1·10 年前
引用 dgnju@m31271n 我是指 Promise 一层层嵌套起来了。 throw 和我直接 return true 和 false 差别不大。 原来的写法,在调用时 getLock('key', 10...

@dgnju 目前 Promise 对分支支持不太友好,用 reject 然后 .then(null,=>console.info()) 捕获怎么样? 用cacth没啥不好的啊,迭代器不也是用的异常实现的嘛?

D
dgnju#2·10 年前

首先谢谢楼上各位的回复。 总结下: 如果需要提前跳出 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 等童鞋的回复

M
MiYogurt#3·10 年前

首先

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);
.....
}

其他的没玩过。

M
MiYogurt#4·10 年前
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)
})


你看下,这个是不是你想要的。

M
m31271n#5·10 年前

「Promise 对象的 then(onfulfilled) 方法」返回「一个以 onfulfilled 函数的返回值进行 resolve 的 Promise 对象」。记住这点,你就可以把嵌套的 Promise 扁平化了。

A
alsotang#6·10 年前

then...return...then...return

D
dgnju#8·10 年前
引用 m31271n「Promise 对象的 then(onfulfilled) 方法」返回「一个以 onfulfilled 函数的返回值进行 resolve 的 Promise 对象」。记住这点,你就可以把嵌套...

@m31271n 假如3个嵌套的 Promise 每一个then 的返回都作为下一个的输入,3个都会执行的情况,很好拉平。到我这例子可能执行完某一个后面就不需要执行了,要能跳出来,困难在这

D
dgnju#9·10 年前
引用 alsotangthen...return...then...return

@alsotang 唐少,请问如果逻辑不需要一路then 到底,中途需要跳出来的情况怎么写呢? ( 如果某一环 reject 跳到 catch 里也觉得很怪)

L
leizongmin#10·10 年前

这Promise写得还不如callback来得简单直接。

D
dgnju#11·10 年前
引用 leizongmin这Promise写得还不如callback来得简单直接。

@leizongmin 主要是这个对象是提供API的,里面十几个函数都是返回 Promise, 想统一啊

M
m31271n#12·10 年前
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。

L
leizongmin#13·10 年前

你需要一个 coroutine 库,比如 https://github.com/leizongmin/lei-coroutine

D
dgnju#14·10 年前
引用 m31271n@dgnju 我觉得可以像上面这样写: ES6 原生的 Promise 不提供 Promise.break 这种方法。但是可以使用 throw 来终止 Promise 链的继续执行。 最后使用...

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

D
dgnju#16·10 年前
引用 leizongmin你需要一个 coroutine 库,比如 https://github.com/leizongmin/lei coroutine

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

D
dgnju#17·10 年前
引用 m31271n@dgnju 确实异常混到一起了。刚刚,又处理了一下。 另外,这样,为什么是嵌套的呢?

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

C
chrislbb#19·10 年前

用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;
        });
    }
S
sanrudongfeng#20·10 年前

提供一种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); }) }) }); 码字不易

参与回复
登录后即可参与回复。登录