কোনও প্রতিশ্রুতি একাধিকবার সমাধান করা কি নিরাপদ?


115

আমার অ্যাপ্লিকেশনটিতে আমার আইআইএনএন পরিষেবা রয়েছে যা নীচের কোডগুলি সহ:

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% জরিমানা হওয়া উচিত। কেবল বোঝার বিষয় হ'ল একবার সমাধান করা (বা প্রত্যাখ্যাত), এটি কোনও পরাজিত বস্তুর জন্য - এটি হয়ে যায় is

যদি আপনি then(...)এর প্রতিশ্রুতিটি আবার কল করতে চান তবে আপনার সাথে সাথে (প্রথম) সমাধান করা / প্রত্যাখ্যাত ফলাফল পাওয়া উচিত।

অতিরিক্ত কলগুলিতে resolve()কোনও প্রভাব নেই (হওয়া উচিত?) আপনি যদি rejectআগে পরাজিত কোনও বস্তুর চেষ্টা করেন তবে কী হবে তা নিশ্চিত নয় resolved(আমি কিছুই সন্দেহ করি না)।


28
এখানে একটি জেএসবিনের চিত্র তুলে ধরেছে যে উপরের সমস্তটি আসলেই সত্য: jsbin.com/gemepay/3/edit?js,console কেবল প্রথম সমাধানটি কখনও ব্যবহৃত হয়।
কনরাড

4
কেউ কি এ সম্পর্কে কোনও অফিসিয়াল ডকুমেন্টেশন পেয়েছেন? এটি এখনই কাজ করে এমনকি অননুমোদিত আচরণের উপর নির্ভর করা অনাকাঙ্ক্ষিত।
3ocene

ecma-international.org/ecma-262/6.0/#sec-promise.resolve - আমার কাছে এমন কিছু খুঁজে পাওয়া যায় নি যা জানায় যে এটি অন্তর্নিহিত UNSAFE। যদি আপনার হ্যান্ডলারটি এমন কিছু করে যা কেবলমাত্র একবারে করা উচিত, তবে আমি আবার এটি সম্পাদন করার আগে এটি পরীক্ষা করে কিছু আপডেট করতে চাই। তবে আমি আরও কিছু অফিসিয়াল এমডিএন প্রবেশ বা স্পষ্ট ডকটিও চাই যা সম্পূর্ণরূপে স্পষ্টতা পেতে পারে।
দেমানিয়াক

আমি প্রতিশ্রুতি + পৃষ্ঠায় "অশান্তি" কিছুই দেখতে পাচ্ছি না। প্রতিশ্রুতি
ডটকম

3
@ ডিমানিয়াক এই প্রশ্নটি প্রতিশ্রুতি / এ + সম্পর্কিত , ইএস 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

প্রতিশ্রুতি একাধিকবার সমাধান করার কোনও সুস্পষ্ট উপায় নেই কারণ এটি সমাধান হয়ে যাওয়ার পরে done এখানে আরও ভাল পন্থাটি পর্যবেক্ষক-পর্যবেক্ষণযোগ্য প্যাটার্ন ব্যবহার করা উদাহরণস্বরূপ আমি নিম্নলিখিত কোডটি লিখেছিলাম যা সকেট ক্লায়েন্টের ইভেন্টটি পর্যবেক্ষণ করে। আপনার প্রয়োজন মেটাতে আপনি এই কোডটি প্রসারিত করতে পারেন

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

আপনার যা করা উচিত তা হ'ল আপনার প্রধান এনজি-আউটলেটে একটি এনজি-ইফ লাগান এবং পরিবর্তে একটি লোডিং স্পিনার দেখান। আপনার লোকেলটি লোড হয়ে গেলে আপনি আউটলেটটি দেখান এবং উপাদানটির স্তরক্রমটি রেন্ডার করতে দিন। এইভাবে আপনার সমস্ত অ্যাপ্লিকেশন ধরে নিতে পারে যে লোকেল লোড হয়েছে এবং কোনও চেক প্রয়োজন নেই।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.