क्या एक वादे को कई बार हल करना सुरक्षित है?


115

मेरे पास अपने आवेदन में 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और वादे के हल होने की प्रतीक्षा करेगा । लेकिन यह देखते हुए कि फ़ंक्शन का उद्देश्य केवल यह सुनिश्चित करना है कि लोकेल लोड किया गया है, यह उपयोगकर्ता के लिए कई बार आह्वान करने के लिए पूरी तरह से ठीक होगा।

मैं वर्तमान में सिर्फ एक ही वादा जमा कर रहा हूं और इसे हल कर सकता हूं यदि उपयोगकर्ता सर्वर से फिर से प्राप्त होने के बाद फ़ंक्शन को फिर से कॉल करता है।

मैं जो बता सकता हूं, वह इस उद्देश्य के अनुसार काम कर रहा है, लेकिन मैं सोच रहा हूं कि क्या यह एक उचित दृष्टिकोण है।



मैंने भी इसका इस्तेमाल किया है और यह ठीक काम करता है।
चंद्रमणि

जवाबों:


119

जैसा कि मैं वर्तमान में वादों को समझता हूं, यह 100% ठीक होना चाहिए। केवल समझने वाली बात यह है कि एक बार हल होने (या अस्वीकार) के बाद, यह एक आस्थगित वस्तु के लिए है - यह किया जाता है।

यदि आपको then(...)इसे फिर से वादा करने पर कॉल करना चाहिए , तो आपको तुरंत (पहले) हल / अस्वीकृत परिणाम प्राप्त करना चाहिए।

अतिरिक्त कॉल का resolve()कोई प्रभाव नहीं होना चाहिए (नहीं होना चाहिए?)। यह निश्चित नहीं है कि यदि आप rejectपहले की गई ऑब्जेक्ट को हटाने का प्रयास करते हैं, तो resolved(मुझे कुछ भी संदेह नहीं है)।


28
यहाँ एक JSBin दर्शाया गया है कि उपरोक्त सभी वास्तव में सच है: jsbin.com/gemepay/3/edit?js,console केवल पहला संकल्प कभी उपयोग किया जाता है।
कोनराड

4
क्या किसी ने इस बारे में कोई आधिकारिक दस्तावेज पाया है? यह आमतौर पर अनजाने व्यवहार पर भरोसा करने में असमर्थ है, भले ही यह अभी काम करता है।
3ocene

ecma-international.org/ecma-262/6.0/#sec-promise.resolve - मुझे आज तक ऐसा कुछ भी नहीं मिला है जिसमें कहा गया हो कि यह स्वाभाविक है। यदि आपका हैंडलर कुछ ऐसा करता है जो वास्तव में केवल ONCE किया जाना चाहिए, तो मुझे फिर से कार्रवाई करने से पहले कुछ राज्य की जांच और अपडेट करना होगा। लेकिन मैं पूर्ण आधिकारिकता प्राप्त करने के लिए कुछ आधिकारिक एमडीएन प्रविष्टि या स्पेक डॉक भी चाहूंगा।
दानिक

मैं PromiseA + पृष्ठ में कुछ भी "परेशान" नहीं देख सकता। वादों को
Com

3
@demaniak यह सवाल वादा / ए + के बारे में है, ईएस 6 वादों के बारे में नहीं। लेकिन आपके सवाल का, बाहरी संकल्प के बारे में ES6 कल्पना का हिस्सा / जवाब देने के लिए अस्वीकार सुरक्षित किया जा रहा है यहाँ
ट्रेवर रॉबिन्सन

1

मैंने थोड़ी देर पहले एक ही बात का सामना किया, वास्तव में एक वादा केवल एक बार हल किया जा सकता है, दूसरा प्रयास कुछ भी नहीं करेगा (कोई त्रुटि नहीं, कोई चेतावनी नहीं, कोई thenआह्वान नहीं )।

मैंने इसे इस तरह से काम करने का फैसला किया:

getUsers(users => showThem(users));

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

बस एक कॉलबैक के रूप में अपने फ़ंक्शन को पास करें और इसे जितनी बार चाहें उतने बार आमंत्रित करें! आशा है कि समझ में आता है।


मुझे लगता है कि यह गलत है। आप बस उस वादे को वापस कर सकते हैं getUsersऔर फिर .then()उस वादे को लागू कर सकते हैं, जितनी बार आप चाहते हैं। कॉलबैक पास करने की आवश्यकता नहीं है। मेरी राय में वादों के फायदों में से एक यह है कि आपको कॉलबैक को सामने रखने की आवश्यकता नहीं है।
जॉन हेंकेल

@ जॉनहोनेल यह विचार कई बार वादे को हल करने का है, यानी कई बार डेटा लौटाएं, एक से अधिक .thenकथन नहीं। इसके लायक होने के लिए, मुझे लगता है कि कॉलिंग संदर्भ में कई बार डेटा वापस करने का एकमात्र तरीका कॉलबैक का उपयोग करना है न कि वादे, क्योंकि वादे उस तरीके से काम करने के लिए नहीं बनाए गए थे।
टी। रेक्स

0

आप वादा की वापसी मूल्य को बदलने की जरूरत है, तो बस में नए मान 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);
});


0

कई बार वादों को हल करने का कोई स्पष्ट तरीका नहीं है क्योंकि जब से यह हल हो गया है। यहाँ बेहतर तरीका यह है कि उदाहरण के लिए ऑब्ज़र्वर-ऑब्जर्वेबल पैटर्न का उपयोग करना है, मैंने निम्नलिखित कोड लिखा है जो सॉकेट क्लाइंट इवेंट को देखता है। अपनी जरूरत पूरी करने के लिए आप इस कोड को बढ़ा सकते हैं

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

व्यवहार की पुष्टि के लिए आप परीक्षण लिख सकते हैं।

निम्नलिखित परीक्षण चलाकर आप यह निष्कर्ष निकाल सकते हैं

संकल्प () / अस्वीकार () कॉल कभी भी त्रुटि नहीं फेंकते हैं।

एक बार निपटाने (अस्वीकार) के बाद, निर्धारित मूल्य (अस्वीकृत त्रुटि) को निम्न संकल्प () या अस्वीकार () कॉल की परवाह किए बिना संरक्षित किया जाएगा।

आप विवरण के लिए मेरे ब्लॉग पोस्ट को भी देख सकते हैं ।

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

})

-1

आपको जो करना चाहिए वह अपने मुख्य एनजी-आउटलेट पर एक एनजी-अगर डाल दिया गया है और इसके बजाय एक लोडिंग स्पिनर दिखाना चाहिए। एक बार जब आपका लोकेल लोड हो जाता है तो आप आउटलेट दिखाते हैं और घटक पदानुक्रम को प्रस्तुत करते हैं। इस तरह से आपके सभी एप्लिकेशन यह मान सकते हैं कि लोकेल लोड है और कोई भी चेक आवश्यक नहीं है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.