sourcecode

약속을 여러 번 해결하는 것이 안전한가?

codebag 2023. 3. 14. 21:36
반응형

약속을 여러 번 해결하는 것이 안전한가?

어플리케이션에 다음 코드가 포함된 i18n 서비스가 있습니다.

var i18nService = function() {
  this.ensureLocaleIsLoaded = function() {
    if( !this.existingPromise ) {
      this.existingPromise = $q.defer();

      var deferred = this.existingPromise;
      var userLanguage = $( "body" ).data( "language" );
      this.userLanguage = userLanguage;

      console.log( "Loading locale '" + userLanguage + "' from server..." );
      $http( { method:"get", url:"/i18n/" + userLanguage, cache:true } ).success( function( translations ) {
        $rootScope.i18n = translations;
        deferred.resolve( $rootScope.i18n );
      } );
    }

    if( $rootScope.i18n ) {
      this.existingPromise.resolve( $rootScope.i18n );
    }

    return this.existingPromise.promise;
  };

가 ""를 것입니다.ensureLocaleIsLoaded약속이 풀릴 때까지 기다리겠습니다.단, 이 함수의 목적은 로케일이 로딩되어 있는지 확인하는 것뿐이므로 사용자가 여러 번 로케일을 호출해도 문제 없습니다.

현재 하나의 약속만 저장하고 있으며, 서버에서 로케일이 정상적으로 검색된 후 사용자가 함수를 다시 호출하면 해결합니다.

제가 봤을 때, 이것은 의도대로 작동하고 있는 것 같습니다만, 이것이 적절한 어프로치인지 궁금합니다.

현시점에서는 약속대로라면 100% OK입니다.이해해야 할 유일한 것은 일단 해결(또는 거부)되면 배변된 물체에 대한 것으로, 그것은 완료된다는 것입니다.

「 」에 then(...)다시 한번 약속하면, 즉시 해결/불합치한 결과를 얻을 수 있습니다.

에 대한 콜resolve()아무 효과도 없을 겁니다.

다음은 이러한 사용 사례에 대한 실행 가능한 스니펫입니다.

var p = new Promise((resolve, reject) => {
  resolve(1);
  reject(2);
  resolve(3);
});

p.then(x => console.log('resolved to ' + x))
 .catch(x => console.log('never called ' + x));

p.then(x => console.log('one more ' + x));
p.then(x => console.log('two more ' + x));
p.then(x => console.log('three more ' + x));

했지만, 은 한되고, 한 번 이 없다( 경고도 않는다). 사실 약속은 한 번만 해결된다. 또 한 번 시도해도 아무 소용이 없다(오류도 경고도 하지 않는다.then★★★★★★★★★★★★★★★★★★」

저는 이렇게 해결하기로 했습니다.

getUsers(users => showThem(users));

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

기능을 콜백으로 전달하고 원하는 횟수만큼 호출하기만 하면 됩니다.그게 말이 됐으면 좋겠어요.

약속은 해결되었으므로 여러 번 해결할 수 있는 명확한 방법은 없습니다.여기서 더 나은 방법은 관찰자-관찰 가능한 패턴을 사용하는 것입니다. 예를 들어 소켓클라이언트 이벤트를 관찰하는 다음 코드를 작성합니다.필요에 따라 이 코드를 확장할 수 있습니다.

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

해야 할 새 하십시오.then에 쇠사슬을 매다then/catch 있다

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

동작을 확인하기 위한 테스트를 작성할 수 있습니다.

다음 테스트를 실행하면 다음과 같은 결론을 내릴 수 있습니다.

resolve()/reject() 콜의 never throw 에러가 발생했습니다.

해결(거부)되면 해결된 값(거부된 오류)은 다음 resolve() 또는 reject() 호출에 관계없이 유지됩니다.

자세한 내용은 제 블로그 투고에서도 확인하실 수 있습니다.

/* 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)
})

})

메인 ng 콘센트에 ng-if를 붙이고 대신 로딩 스피너를 표시해야 합니다.로케일이 로딩되면 아웃렛을 표시하고 컴포넌트 계층이 렌더링되도록 합니다.이렇게 하면 모든 응용 프로그램이 로케일이 로드되어 검사가 필요하지 않다고 가정할 수 있습니다.

아니요. 약속을 여러 번 해결하거나 거부하는 것은 안전하지 않습니다.기본적으로는 버그로, 항상 재현할 수 있는 것은 아니기 때문에 잡기 어렵습니다.

디버깅 시 이러한 문제를 추적하기 위해 사용할 수 있는 패턴이 있습니다. 주제에 대한 훌륭한 강의: Ruben Bridgewater — 오류 처리: 올바르게 하기! (질문과 관련된 부분은 약 40분)

github gest: reuse_module을 참조하십시오.js

/*
reuse a promise for multiple resolve()s since promises only resolve once and then never again
*/

import React, { useEffect, useState } from 'react'

export default () => {
    
    const [somePromise, setSomePromise] = useState(promiseCreator())
        
    useEffect(() => {
        
        somePromise.then(data => {
            
            // do things here
            
            setSomePromise(promiseCreator())
        })
        
    }, [somePromise])
}

const promiseCreator = () => {
    return new Promise((resolve, reject) => {
        // do things
        resolve(/*data*/)
    })
}

언급URL : https://stackoverflow.com/questions/20328073/is-it-safe-to-resolve-a-promise-multiple-times

반응형