如何在Promise外部控制其状态

请问如何避免报错呢?
我应该思路就是错的, 求解决思路

var obj = {
    ok: null,
    cancel: null
};

function fn() {
    return new Promise((resolve, reject) => {
        obj.ok = resolve;
        obj.cancel = reject;
    });
}

fn();
obj.cancel(); //  Uncaught (in promise) undefined
阅读 12.1k
6 个回答

先直接说代码的问题,在于 Promise 没有 catch,可以

fn()
    .then(() => {
        // ... resolve 之后的事情
        // 可以省略不要
    })
    .catch(() => {
        // ... reject 之后的事情
    });

或者直接在 then 的时候指定第二个回调

fn()
    .then(() => {
        // ... resolve 之后的事情
        // 可以省略不要
    }, () => {
        // ... reject 之后的事情
    });

然后来说说这个设计思路的问题

Promise 是一次性的,也就是说,如果你调用了 resolve 或者 reject 这个 Promise 会处理相关回调,然后它的生命周期就结束了。那么在这种情况下,你把 resolvereject 赋给 obj 作为属性使用,就不符合“一次性”,因为可以通过 obj 多次调用。所以这个设计思路本身是有问题的

然后,从你的代码我看不出来你是想干啥。Promise 用于处理异步调用,那一定是存在异步调用才需要处理,所以要从异步调用的点开始去思考。

比如,有一个 click 事件,它是一个异步调用(用户什么时候点击并不知道)。一般操作是为这个单击事件写回调,单击时触发。如果你想用 Promise,就需要在 Promise 里设置单击事件,并在这个事件触发之后,调用 Promise 的 resolvereject,同时,由于 Promise 是一次性的,所以还得注销这个事件,过程是这样

const waitClick = new Promise((resolve, reject) => {
    function handler(e) {
        $("#something").off("click", handler);
        if (somethingRight) {
            resolve(e);
        } else {
            reject(e);
        }
    }

    $("#something").on("click", handler);
});

waitClick
    .then(e => {

    })
    .catch(e => {

    });

当然这里单击事件只是为了表示异步,实际上事件处理直接用回调更方便。所以用 Promise,一定要找到你异步的起点在哪里,不然 Promise 没啥意义。


最后,如果有空可以了解下 async/await,Node 7.6 之后就完全支持了,用它以同步形式的代码写异步会方便得多。

Promise 对象的状态不受外界影响。Promise
对象代表一个异步操作,有三种状态:Pending(进行中)、Resolved(已完成,又称 Fulfilled)和
Rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是 Promise
这个名字的由来,它的英语意思就是「承诺」,表示其他手段无法改变。

var obj = {
    ok: null,
    cancel: null
};

function fn() {
    return new Promise((resolve, reject) => {
        obj.ok = resolve;
        obj.cancel = reject;
    });
}
fn().then(d=>console.log("then "+d)).catch(d=>console.log("catch " + d));

然后

obj.cancel("cancel")

输出

catch cancel

但是并不推荐这样用,因为一个Promise`resolve或者reject之后其状态就不会再发生改变了,所以这时obj所保留的两个对resolvereject`的引用就完全没有存在的意义了

var obj = {

ok: null,
cancel: null

};
let fn=new Promise((resolve, reject)=>{

    resolve(obj.ok=resolve)
    reject(obj.cancel = reject)
}); 

fn.then(function(v){ console.log(v)});

新手上路,请多包涵

vue里可以这样用

export default {
  methods: {
    open(title: string) {
      this.shown = true
      this.popupTitle = title
      return new Promise((resolve, reject) => {
        this.$on('onOk', resolve)
        this.$on('onCancel', reject)
      })
        .then(() => {})
        .catch(() => {})
    },
    cancel() {
      this.$emit('onCancel')
    },
    ok() {
      this.$emit('onOk')
    }
  },
}
新手上路,请多包涵

首先我赞成 @边城 的说法,Promise不是楼主这么用的;

其次,就技术研讨来说,楼主想要达成的,通过obj来从外部控制promise状态的事其实是可以办到的:
1、Promise的状态取决于promise主体中是否调用了resolved或者reject或者在调用resolved和reject前就发生异常(将会直接进入reject状态)
2、Promise也是js对象(虽然它实际是个函数)、逃不开按地址引用带来的副作用,所以楼主想要达到的想法是可以实现的,楼主写的代码是想调用reject,但是没有设置fn的reject,或者catch,所以会报错。Promise允许你不设置resolved方法(即如果楼主直接调用obj.ok()是没问题的,不会报错,promise状态也会变为resolved)、但如果出现异常或者主动调用进入reject而没有设置reject方法或catch方法,则会报错。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏