Is it safe to resolve a promise multiple times?

前端 未结 7 845
旧时难觅i
旧时难觅i 2021-01-31 06:33

I have an i18n service in my application which contains the following code:

var i18nService = function() {
  this.ensureLocaleIsLoaded = function() {
    if( !th         


        
相关标签:
7条回答
  • 2021-01-31 07:09

    There s no clear way to resolve promises multiple times because since it's resolved it's done. The better approach here is to use observer-observable pattern for example i wrote following code that observes socket client event. You can extend this code to met your need

    const evokeObjectMethodWithArgs = (methodName, args) => (src) => src[methodName].apply(null, args);
        const hasMethodName = (name) => (target = {}) => typeof target[name] === 'function';
        const Observable = function (fn) {
            const subscribers = [];
            this.subscribe = subscribers.push.bind(subscribers);
            const observer = {
                next: (...args) => subscribers.filter(hasMethodName('next')).forEach(evokeObjectMethodWithArgs('next', args))
            };
            setTimeout(() => {
                try {
                    fn(observer);
                } catch (e) {
                    subscribers.filter(hasMethodName('error')).forEach(evokeObjectMethodWithArgs('error', e));
                }
            });
    
        };
    
        const fromEvent = (target, eventName) => new Observable((obs) => target.on(eventName, obs.next));
    
        fromEvent(client, 'document:save').subscribe({
            async next(document, docName) {
                await writeFilePromise(resolve(dataDir, `${docName}`), document);
                client.emit('document:save', document);
            }
        });
    
    0 讨论(0)
  • 2021-01-31 07:12

    No. It is not safe to resolve/reject promise multiple times. It is basically a bug, that is hard to catch, becasue it can be not always reproducible.

    There is pattern that can be used to trace such issues in debug time. Great lecture on this topic: Ruben Bridgewater — Error handling: doing it right! (the part related to the question is around 40 min)

    0 讨论(0)
  • 2021-01-31 07:16

    What you should do is put an ng-if on your main ng-outlet and show a loading spinner instead. Once your locale is loaded the you show the outlet and let the component hierarchy render. This way all of your application can assume that the locale is loaded and no checks are necessary.

    0 讨论(0)
  • 2021-01-31 07:18

    You can write tests to confirm the behavior.

    By running the following test you can conclude that

    The resolve()/reject() call never throw error.

    Once settled (rejected), the resolved value (rejected error) will be preserved regardless of following resolve() or reject() calls.

    You can also check my blog post for details.

    /* eslint-disable prefer-promise-reject-errors */
    const flipPromise = require('flip-promise').default
    
    describe('promise', () => {
        test('error catch with resolve', () => new Promise(async (rs, rj) => {
            const getPromise = () => new Promise(resolve => {
                try {
                    resolve()
                } catch (err) {
                    rj('error caught in unexpected location')
                }
            })
            try {
                await getPromise()
                throw new Error('error thrown out side')
            } catch (e) {
                rs('error caught in expected location')
            }
        }))
        test('error catch with reject', () => new Promise(async (rs, rj) => {
            const getPromise = () => new Promise((_resolve, reject) => {
                try {
                    reject()
                } catch (err) {
                    rj('error caught in unexpected location')
                }
            })
            try {
                await getPromise()
            } catch (e) {
                try {
                    throw new Error('error thrown out side')
                } catch (e){
                    rs('error caught in expected location')
                }
            }
        }))
        test('await multiple times resolved promise', async () => {
            const pr = Promise.resolve(1)
            expect(await pr).toBe(1)
            expect(await pr).toBe(1)
        })
        test('await multiple times rejected promise', async () => {
            const pr = Promise.reject(1)
            expect(await flipPromise(pr)).toBe(1)
            expect(await flipPromise(pr)).toBe(1)
        })
        test('resolve multiple times', async () => {
            const pr = new Promise(resolve => {
                resolve(1)
                resolve(2)
                resolve(3)
            })
            expect(await pr).toBe(1)
        })
        test('resolve then reject', async () => {
            const pr = new Promise((resolve, reject) => {
                resolve(1)
                resolve(2)
                resolve(3)
                reject(4)
            })
            expect(await pr).toBe(1)
        })
        test('reject multiple times', async () => {
            const pr = new Promise((_resolve, reject) => {
                reject(1)
                reject(2)
                reject(3)
            })
            expect(await flipPromise(pr)).toBe(1)
        })
    
        test('reject then resolve', async () => {
            const pr = new Promise((resolve, reject) => {
                reject(1)
                reject(2)
                reject(3)
                resolve(4)
            })
            expect(await flipPromise(pr)).toBe(1)
        })
    test('constructor is not async', async () => {
        let val
        let val1
        const pr = new Promise(resolve => {
            val = 1
            setTimeout(() => {
                resolve()
                val1 = 2
            })
        })
        expect(val).toBe(1)
        expect(val1).toBeUndefined()
        await pr
        expect(val).toBe(1)
        expect(val1).toBe(2)
    })
    
    })
    
    0 讨论(0)
  • 2021-01-31 07:19

    I faced the same thing a while ago, indeed a promise can be only resolved once, another tries will do nothing (no error, no warning, no then invocation).

    I decided to work it around like this:

    getUsers(users => showThem(users));
    
    getUsers(callback){
        callback(getCachedUsers())
        api.getUsers().then(users => callback(users))
    }
    

    just pass your function as a callback and invoke it as many times you wish! Hope that makes sense.

    0 讨论(0)
  • 2021-01-31 07:33

    If you need to change the return value of promise, simply return new value in then and chain next then/catch on it

    var p1 = new Promise((resolve, reject) => { resolve(1) });
        
    var p2 = p1.then(v => {
      console.log("First then, value is", v);
      return 2;
    });
        
    p2.then(v => {
      console.log("Second then, value is", v);
    });

    0 讨论(0)
提交回复
热议问题