Redux: ما هو مصطلح redux لانتظار مكالمات متعددة غير متزامنة؟

تم إنشاؤها على ١٤ سبتمبر ٢٠١٥  ·  30تعليقات  ·  مصدر: reduxjs/redux

عندما أقوم ببدء مكون رد الفعل ، أحتاج إلى إجراء مكالمتين http غير متزامنتين مختلفتين وانتظر نتائجهما.
ستضع هاتان النداءان بياناتهما بشكل مثالي في مخفضين مختلفين.

أنا أفهم نمط إجراء مكالمة غير متزامنة واحدة.
ومع ذلك ، لا أرى أي عينات جيدة لإجراء مكالمات متعددة غير متزامنة ، وجعلها تنفذ بالتوازي وانتظر وصول نتائجها.

ما هي طريقة إعادة القيام بذلك؟

question

التعليق الأكثر فائدة

أفترض أنك تستخدم برمجيات Redux Thunk الوسيطة.

أعلن عن بعض المبدعين:

import 'babel-core/polyfill'; // so I can use Promises
import fetch from 'isomorphic-fetch'; // so I can use fetch()

function doSomething() {
  return dispatch => 
    fetch(
      '/api/something'
    ).then(
      response => response.json()
    ).then(
      json => dispatch({ type: DO_SOMETHING, json }),
      err => dispatch({ type: SOMETHING_FAILED, err })
    );
}

function doSomethingElse() {
  return dispatch => 
    fetch(
      '/api/something'
    ).then(
      response => response.json()
    ).then(
      json => dispatch({ type: DO_SOMETHING_ELSE, json }),
      err => dispatch({ type: SOMETHING_ELSE_FAILED, err })
    );
}

لاحظ كيف أجعلها نقطة لإرجاع Promise في النهاية عن طريق الاحتفاظ بالسلسلة كقيمة إرجاع للوظيفة داخل منشئي إجراء thunk. هذا يتيح لي القيام بذلك:

store.dispatch(doSomething()).then(() => {
  console.log('I did something');
});

عند استخدام Redux Thunk ، يُرجع dispatch قيمة الإرجاع للوظيفة التي قمت بإرجاعها من منشئ إجراء thunk - في هذه الحالة ، الوعد.

يتيح لك هذا استخدام أدوات دمج مثل Promise.all() لانتظار اكتمال العديد من الإجراءات:

Promise.all([
  store.dispatch(doSomething()),
  store.dispatch(doSomethingElse())
]).then(() => {
  console.log('I did everything!');
});

ومع ذلك ، يُفضل تحديد منشئ إجراء آخر يعمل بمثابة _تكوين_ لمنشئي الإجراء السابقين ، واستخدام Promise.all داخليًا:

function doEverything() {
  return dispatch => Promise.all([
    dispatch(doSomething()),
    dispatch(doSomethingElse())
  ]);
}

الآن يمكنك فقط الكتابة

store.dispatch(doEverything()).then(() => {
  console.log('I did everything!');
});

إيفاد سعيد!

ال 30 كومينتر

أفترض أنك تستخدم برمجيات Redux Thunk الوسيطة.

أعلن عن بعض المبدعين:

import 'babel-core/polyfill'; // so I can use Promises
import fetch from 'isomorphic-fetch'; // so I can use fetch()

function doSomething() {
  return dispatch => 
    fetch(
      '/api/something'
    ).then(
      response => response.json()
    ).then(
      json => dispatch({ type: DO_SOMETHING, json }),
      err => dispatch({ type: SOMETHING_FAILED, err })
    );
}

function doSomethingElse() {
  return dispatch => 
    fetch(
      '/api/something'
    ).then(
      response => response.json()
    ).then(
      json => dispatch({ type: DO_SOMETHING_ELSE, json }),
      err => dispatch({ type: SOMETHING_ELSE_FAILED, err })
    );
}

لاحظ كيف أجعلها نقطة لإرجاع Promise في النهاية عن طريق الاحتفاظ بالسلسلة كقيمة إرجاع للوظيفة داخل منشئي إجراء thunk. هذا يتيح لي القيام بذلك:

store.dispatch(doSomething()).then(() => {
  console.log('I did something');
});

عند استخدام Redux Thunk ، يُرجع dispatch قيمة الإرجاع للوظيفة التي قمت بإرجاعها من منشئ إجراء thunk - في هذه الحالة ، الوعد.

يتيح لك هذا استخدام أدوات دمج مثل Promise.all() لانتظار اكتمال العديد من الإجراءات:

Promise.all([
  store.dispatch(doSomething()),
  store.dispatch(doSomethingElse())
]).then(() => {
  console.log('I did everything!');
});

ومع ذلك ، يُفضل تحديد منشئ إجراء آخر يعمل بمثابة _تكوين_ لمنشئي الإجراء السابقين ، واستخدام Promise.all داخليًا:

function doEverything() {
  return dispatch => Promise.all([
    dispatch(doSomething()),
    dispatch(doSomethingElse())
  ]);
}

الآن يمكنك فقط الكتابة

store.dispatch(doEverything()).then(() => {
  console.log('I did everything!');
});

إيفاد سعيد!

gaearon هل هناك أي سبب معين يجعلك تفضل thunk على promise الوسيطة؟ أو نصيحة حول متى تستخدم ماذا؟ شكر.

لا يوجد سبب محدد ، أنا أكثر دراية به.
جرب كلاهما واستخدم ما يناسبك!

فهم، وذلك بفضل!

JFYI :)
screen shot 2015-09-14 at 11 06 42 am

إذا كنت تريد يمكنك إلقاء نظرة عليه https://github.com/Cron-J/redux-async-transitions

gaearon سؤال سريع - بالنظر إلى المثال أعلاه ، حيث تتصل:

store.dispatch(doSomething()).then(() => {
  console.log('I did something');
});

- يبدو أنه سيتم ضرب كتلة "فعلت شيئًا ما" حتى إذا تم رفض doSomething - نظرًا لأنك لا تقوم بإعادة طرح الخطأ في سلسلة doSomething بعد أن تمسك به. هل هذا متعمد؟ إذا لم يكن كذلك - وإذا كنت تريد إعادة طرح الخطأ هناك بدلاً من ذلك - فهل ستتعامل معه مرة أخرى في المتصل؟

إنه مجرد مثال ، لا أعني أن هذا مرجع نهائي ، فقط كطريقة للنظر إليه. لم أعطيها دقيقة من التفكير الحقيقي.

أنت تعرف كيف ترد الوعود من صانعي العمل ، والباقي متروك لك. ما إذا كنت تريد إعادة سرد الأخطاء ، ومكان التعامل معها ، فهذا متروك لك. هذا ليس من اهتمامات Redux حقًا ، لذا فإن نصيحتي جيدة مثل أي شخص آخر ، وفي هذه الحالة يكون لديك سياق أكثر بكثير حول ما تريد بناءه والأسلوب الذي تفضله أكثر من أي وقت مضى.

رائع شكرا! اعتقدت أن هذا هو الحال ، لكنني أردت التأكد من عدم وجود بعض السحر الذي كنت أتجاهله.

نظرًا لأن Redux قابل للتخصيص ، فإن قيمة الإرجاع dispatch ليست مضمونة لتكون وعدًا متوقعًا.

في الواقع ، واجهت مشكلة واحدة في الحياة الواقعية منذ أن أخذت حلقة إعادة التشغيل. تقوم بتعديل dispatch لإرجاع وعد ملفوف (https://github.com/raisemarketplace/redux-loop/blob/master/modules/effects.js#L38). لذلك ، لا يمكننا الاعتماد على قيمة الإرجاع البالغة dispatch .

حسنًا ، كما قال دان أعلاه في هذا الموضوع: إنه تطبيقك ، أنت تكتب صانعي الإجراءات ، لذا عليك أن تقرر ما سيعودون إليه.

markerikson قيمة الإرجاع dispatch غير مضمونة لتكون قيمة مرتجعة لمنشئ الإجراء ، لأن البرمجيات الوسيطة قد تعدلها.

لكن لدي حل بديل. بدلاً من السلسلة then على dispatch(actonCreator()) ، يمكننا السلسلة على actionCreator() .

نعم ، يمكن للبرمجيات الوسيطة أن تعدل الأشياء ، لكن وجهة نظري هي أنك أنت تقوم بإعداد البرمجيات الوسيطة في تطبيقك :)

مرحبًا gaearon ،

function fetchDataBWhichDependsOnA() {
  return dispatch => {
    dispatch(fetchDataA())
      .then(() => {
        fetch('/api/get/data/B')
          .then(dispatch(receiveDataB(response.data)))
      })
  }
}

هناك إجراءان متداخلان غير متزامنين في مقتطف الشفرة هذا ، يعملان ولكني أواجه مشكلة في اختباره. يمكنني اختبار fetchDataA() لأنه يحتوي على مستوى واحد فقط من المكالمات غير المتزامنة. ولكن عندما كنت أحاول كتابة اختبار لـ fetchDataBWhichDependsOnA() ، لم أتمكن من استرداد أحدث القيم من كتلة store في then .

timothytong أنت لا تعيد الوعود. إذا قمت بإرجاع النتيجة من dispatch والمكالمة المتداخلة fetch يجب أن تكون قادرًا على الانتظار في اختباراتك حتى يتم حل السلسلة بأكملها.

@ johanneslumpe صيد جيد ، شكرا يا

واجهت نفس المشكلة وأمضيت 30 دقيقة أحاول التوصل إلى حل. لم أنتهي من هذا التنفيذ ، لكني أشعر أنه يسير على الطريق الصحيح.

https://github.com/Sailias/sync-thunk

فكرتي هي أن أحد المكونات يمكن أن يطلب تعبئة حالات إعادة معينة ، إذا لم تكن كذلك ، فيجب أن تشير إلى خريطة لاستدعاء الإجراءات لملئها وربطها بـ then . لا يحتاج كل إجراء في السلسلة إلى بيانات تم تمريرها ، بل يمكنه فقط أخذ البيانات من الحالة لأن الإجراءات التابعة كانت ستملأها.

تضمين التغريدة

Promise.all([
  store.dispatch(doSomething()),
  store.dispatch(doSomethingElse())
]).then(() => {
  console.log('I did everything!');
});

لدي سؤال حول هذا. آسف إذا كان ذلك أساسيًا.

في هذا السيناريو ، سيكون هناك إرسالان. هذا يعني أنه سيتم عرض المكون مرتين. ولكن ، ماذا لو أراد المكون أن تظهر كلتا البيانات شيئًا ذا مغزى؟

لذلك ، في هذا السيناريو ، أود أن يتم عرض المكون مرة واحدة فقط وذلك عندما تكتمل جميع الإجراءات غير المتزامنة.

لقد حصلت على أسماء قليلة من البرامج الوسيطة التي يبدو أنها تتعامل مع هذه المشكلة مثل إجراءات الاشتراك المُجمّع وإعادة التجميع . لكن ، مرتبك قليلاً بشأن أفضل نهج.

إذا كان هذا سيناريو صحيحًا ، فما هي الطريقة المقترحة للتعامل مع هذا؟

@ jintoppy إذا كان سؤالك هو تجنب المكالمات المتعددة لتقديم مكون ، فلن يساعدك الحل Promise.all ؛ لأن كلا من doSomething() and doSomething() جميع بيانات الإرسال التي قد تؤدي إلى تغيير الحالة وتتسبب في عرض المكون.

jintoppy لماذا تحتاج إلى أي برامج وسيطة للتعامل مع وضعك؟
ما الذي يمنعك من القيام بشيء من هذا القبيل.

// action creator
function fetchAB() {
  return dispatch => {
    const fetchA = fetch( 'api/endpoint/A' );
    const fetchB = fetch( 'api/endpoint/B' );
    return Promise.all([ fetchA, fetchB ])
      .then( values => dispatch(showABAction(values)) )
      .catch( err => throw err );
  }
} 

يمكنك فقط وضع كل طلبك في منشئ الإجراء الواحد وإطلاق الإجراء بمجرد حلها جميعًا. في هذه الحالة ، يتم استدعاء المخفض وبالتالي تم تغيير الحالة مرة واحدة فقط ، لذلك من المحتمل أن يتم تشغيل العرض () مرة واحدة فقط.

apranovich : أرى مشكلة واحدة في هذا النهج. الآن ، أحتاج إلى الجمع بين عمليات الجلب المتعددة في إجراء واحد. لذا ، إذا كان لدي ، دعنا نقول 10 مكالمات غير متزامنة ، يجب أن أضع كل هذه في إجراء واحد. أيضًا ، ماذا لو أردت تشغيل واحد من هذه المكالمات غير المتزامنة العشرة لاحقًا؟ في هذا النهج ، في الأساس ، سوف يكمن كل منطقك غير المتزامن في إجراء واحد.

شكرا لكم جميعا على المعلومات المفيدة. سؤال سريع ، كيف يمكن تحقيق نفس الشيء ولكن بدلاً من ذلك عندما يقوم أحد عمليات الجلب بإرجاع البيانات المطلوبة بواسطة طريقة الجلب التالية. على سبيل المثال ، هناك نقطة نهاية مجانية لواجهة برمجة تطبيقات الطقس حيث أقوم بإدخال الموقع الجغرافي للحصول على معرف (أول fetch ) وهو مطلوب لـ fetch للحصول على طقس الموقع. أعلم أن هذه حالة استخدام فردية ، لكني أرغب في معرفة كيفية التعامل مع مواقف مماثلة. شكرا لك مقدما!

مرحبًا gaearon ، أرغب في الحصول على رأيك حول سؤالي هذا. لا بد لي من إجراء مكالمات متداخلة غير متزامنة إلى واجهتي API. تعتمد الاستجابة من API الثانية على استجابة الأولى.

export const getApi=()=>{
        return(d)=>{
        axios({
            method:'GET',
            url:Constants.URLConst+"/UserProfile",
            headers:Constants.headers
        }).then((response)=>{
            return d({
                type:GET_API_DATA,
                response,
                axios({
                    method:'GET',
                    url:Constants.URLConst+"/UserProfileImage?enterpriseId="+response.data.ProfileData.EnterpriseId,
                    headers:Constants.headers
                }).then((response1)=>{
                    return d({
                        type:GET_PROFILE_IMAGE,
                        response1
                    })
                })
            })
        }).catch((e)=>{
            console.log("e",e);
        })
    }

}

لقد جربت شيئًا من هذا القبيل أعلاه ، لكن هذا لم ينجح ، وفي مشروعي ، لا بد لي من استخدام الاستجابة من كلا واجهات برمجة التطبيقات بشكل مستقل.
إذن ، ما هي الطريقة الصحيحة للقيام بذلك؟

شكر.

@ c0b41 ، يرجى التحقق من السؤال المحدث.

hey @ c0b41 ، هذا يعطي أخطاء في بناء الجملة في استدعاء axios الثاني.

@ aayushis12 ، @ c0b41 : هذا أداة تعقب أخطاء ، وليس منتدى مساعدة. يجب أن تحاول طرح هذا السؤال على Stack Overflow أو Reactiflux. سيكون هناك المزيد من الأشخاص الذين سيرون السؤال وسيكونون قادرين على المساعدة.

لأولئك الذين يعانون من ذلك

لقد اتممت كل شئ

تتم طباعة قبل حل جميع المكالمات غير المتزامنة ، تحقق مما إذا كنت تقوم بإرجاع عبارة أو تعبير من مُنشئي الإجراءات غير المتزامنة.

على سبيل المثال ، إذا تم التصريح عن doSomethingElse() بـ {} في وظيفة السهم

function doSomethingElse() {
  return dispatch => {
    fetch(
      '/api/something'
    ).then(
      response => response.json()
    ).then(
      json => dispatch({ type: DO_SOMETHING_ELSE, json }),
      err => dispatch({ type: SOMETHING_ELSE_FAILED, err })
    );
  }
}

ستتم طباعة "فعلت كل شيء" قبل حل doSomethingElse() . يمكنك حل هذه المشكلة بإسقاط {} بعد => .

gaearon سؤال واحد ، استنادًا إلى https://github.com/reduxjs/redux/issues/723#issuecomment -139927639 كيف يمكنك التعامل معه إذا كنت بحاجة إلى إرسال إجراء ينتظر إرسال إجراءين _AND_ الولاية تغيرت بسبب تلك الإجراءات؟ لدي حالة يتم فيها إرسال الإجراءات ولكن الإجراء داخل then تم تنفيذه _ قبل _ تغيرت الحالة. نحن نستخدم redux-thunk .

enmanuelduran : لم يعد دان تضغط عليه.

من الصعب الإجابة على سؤالك دون رؤية التعليمات البرمجية ، ولكن لا يُقصد من مشكلاتنا أن تكون منتدى مناقشة. سيكون من الأفضل إذا نشرت سؤالك على Stack Overflow - فمن المحتمل أن تحصل على إجابة أفضل هناك ، بشكل أسرع.

markerikson أوه ، آسف ، لم يكن ذلك في نيتي ، أنشأ سؤالًا في ستيكوفيرفلوو إذا كان شخص ما مهتمًا: https://stackoverflow.com/questions/51846612/dispatch-an-action-after-other-specific-actions-were- إيفاد-وتغيير الدولة

شكرا جزيلا يا أصدقاء.

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات