javascript شرح - لماذا نحتاج إلى برامج وسيطة للتدفق المتزامن في Redux؟




ماهو react (7)

للإجابة على السؤال المطروح في البداية:

لماذا لا يستطيع مكون الحاوية استدعاء واجهة برمجة تطبيقات المتزامن ، ثم إرسال الإجراءات؟

ضع في اعتبارك أن هذه المستندات مخصصة لـ Redux وليس Redux Plus React. يمكن لمخازن Redux المرتبطة بمكونات React أن تفعل بالضبط ما تقوله ، ولكن لا يقبل مخزن Plain Jane Redux بدون أي برامج وسيطة حججًا dispatch باستثناء الكائنات العادية.

دون الوسيطة يمكنك بالطبع لا تزال تفعل

const store = createStore(reducer);
MyAPI.doThing().then(resp => store.dispatch(...));

ولكنها حالة مشابهة حيث يتم التفاف asynchrony حول Redux بدلاً من معالجتها بواسطة Redux. لذا ، فإن الوسيطة تسمح بالتزامن عن طريق تعديل ما يمكن تمريره مباشرة dispatch .

ومع ذلك ، فإن روح اقتراحك ، كما أعتقد ، صحيحة. هناك بالتأكيد طرق أخرى يمكنك التعامل معها في تطبيق Redux + React.

تتمثل إحدى فوائد استخدام البرامج الوسيطة في أنه يمكنك الاستمرار في استخدام منشئي الأحداث كالمعتاد دون القلق بشأن كيفية توصيلهم. على سبيل المثال ، باستخدام redux-thunk ، سيبدو الرمز الذي كتبته يشبه إلى حد كبير

function updateThing() {
  return dispatch => {
    dispatch({
      type: ActionTypes.STARTED_UPDATING
    });
    AsyncApi.getFieldValue()
      .then(result => dispatch({
        type: ActionTypes.UPDATED,
        payload: result
      }));
  }
}

const ConnectedApp = connect(
  (state) => { ...state },
  { update: updateThing }
)(App);

والتي لا تبدو مختلفة تمامًا عن النسخة الأصلية - إنها updateThing قليلاً - connect لا يعرف أن updateThing (أو يجب أن يكون) غير متزامن.

إذا كنت ترغب أيضًا في دعم promises ، أو observables ، أو الملاحم ، أو المولعين بالجنس ، ومبدعي الأعمال التقديريين إلى حد كبير ، فيمكن لـ Redux القيام بذلك فقط من خلال تغيير ما تمرره dispatch (ويعرف أيضًا ما تعود من منشئي الأحداث). لا تلويث مع مكونات React (أو connect المكالمات) اللازمة.

وفقا للمستندات ، "دون الوسيطة ، مخزن Redux يدعم فقط تدفق البيانات المتزامنة" . لا أفهم لماذا هذا هو الحال. لماذا لا يستطيع مكون الحاوية استدعاء واجهة برمجة تطبيقات المتزامن ، ثم dispatch الإجراءات؟

على سبيل المثال ، تخيل واجهة مستخدم بسيطة: حقل وزر. عندما يقوم المستخدم بدفع الزر ، يتم ملء الحقل بالبيانات من خادم بعيد.

import * as React from 'react';
import * as Redux from 'redux';
import { Provider, connect } from 'react-redux';

const ActionTypes = {
    STARTED_UPDATING: 'STARTED_UPDATING',
    UPDATED: 'UPDATED'
};

class AsyncApi {
    static getFieldValue() {
        const promise = new Promise((resolve) => {
            setTimeout(() => {
                resolve(Math.floor(Math.random() * 100));
            }, 1000);
        });
        return promise;
    }
}

class App extends React.Component {
    render() {
        return (
            <div>
                <input value={this.props.field}/>
                <button disabled={this.props.isWaiting} onClick={this.props.update}>Fetch</button>
                {this.props.isWaiting && <div>Waiting...</div>}
            </div>
        );
    }
}
App.propTypes = {
    dispatch: React.PropTypes.func,
    field: React.PropTypes.any,
    isWaiting: React.PropTypes.bool
};

const reducer = (state = { field: 'No data', isWaiting: false }, action) => {
    switch (action.type) {
        case ActionTypes.STARTED_UPDATING:
            return { ...state, isWaiting: true };
        case ActionTypes.UPDATED:
            return { ...state, isWaiting: false, field: action.payload };
        default:
            return state;
    }
};
const store = Redux.createStore(reducer);
const ConnectedApp = connect(
    (state) => {
        return { ...state };
    },
    (dispatch) => {
        return {
            update: () => {
                dispatch({
                    type: ActionTypes.STARTED_UPDATING
                });
                AsyncApi.getFieldValue()
                    .then(result => dispatch({
                        type: ActionTypes.UPDATED,
                        payload: result
                    }));
            }
        };
    })(App);
export default class extends React.Component {
    render() {
        return <Provider store={store}><ConnectedApp/></Provider>;
    }
}

عندما يتم عرض المكون الذي تم تصديره ، يمكنني النقر فوق الزر ويتم تحديث الإدخال بشكل صحيح.

لاحظ وظيفة update في مكالمة connect . يقوم بإرسال تصرف يخبر التطبيق بأنه يتم تحديثه ، ثم يقوم بإجراء اتصال غير متزامن. بعد انتهاء المكالمة ، يتم إرسال القيمة المقدمة كحمولة من إجراء آخر.

ما هو الخطأ في هذا النهج؟ لماذا أرغب في استخدام Redux Thunk أو Redux Promise ، كما تقترح الوثائق؟

تحرير: لقد بحثت في ريبو ريكس عن القرائن ، ووجدت أن "منشئو العمل" مطالبون بوظائف نقية في الماضي. على سبيل المثال ، إليك مستخدم يحاول تقديم تفسير أفضل لتدفق بيانات المتزامن:

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

لم يعد مطلوبًا من منشئي الأعمال أن يكونوا محضين. لذلك ، كان من المؤكد بالتأكيد ، thunk / وعد الوسيطة في الماضي ، ولكن يبدو أن هذا لم يعد هو الحال؟


حسنًا ، لنبدأ في معرفة كيفية عمل البرامج الوسيطة أولاً ، والتي تجيب تمامًا على السؤال ، هذه هي وظيفة رمز التطبيق mediumMidWare في Redux:

function applyMiddleware() {
  for (var _len = arguments.length, middlewares = Array(_len), _key = 0; _key < _len; _key++) {
    middlewares[_key] = arguments[_key];
  }

  return function (createStore) {
    return function (reducer, preloadedState, enhancer) {
      var store = createStore(reducer, preloadedState, enhancer);
      var _dispatch = store.dispatch;
      var chain = [];

      var middlewareAPI = {
        getState: store.getState,
        dispatch: function dispatch(action) {
          return _dispatch(action);
        }
      };
      chain = middlewares.map(function (middleware) {
        return middleware(middlewareAPI);
      });
      _dispatch = compose.apply(undefined, chain)(store.dispatch);

      return _extends({}, store, {
        dispatch: _dispatch
      });
    };
  };
}

انظر إلى هذا الجزء ، انظر كيف أصبح إرسالنا وظيفة .

  ...
  getState: store.getState,
  dispatch: function dispatch(action) {
  return _dispatch(action);
}
  • Note that each middleware will be given the dispatch and getState functions as named arguments.

OK, this is how Redux-thunk as one of the most used middlewares for Redux introduce itself:

Redux Thunk middleware allows you to write action creators that return a function instead of an action. The thunk can be used to delay the dispatch of an action, or to dispatch only if a certain condition is met. The inner function receives the store methods dispatch and getState as parameters.

So as you see, it will return a function instead an action, means you can wait and call it anytime you want as it's a function...

So what the heck is thunk? That's how it's introduced in Wikipedia:

In computer programming, a thunk is a subroutine used to inject an additional calculation into another subroutine. Thunks are primarily used to delay a calculation until it is needed, or to insert operations at the beginning or end of the other subroutine. They have a variety of other applications to compiler code generation and in modular programming.

The term originated as a jocular derivative of "think".

A thunk is a function that wraps an expression to delay its evaluation.

//calculation of 1 + 2 is immediate 
//x === 3 
let x = 1 + 2;

//calculation of 1 + 2 is delayed 
//foo can be called later to perform the calculation 
//foo is a thunk! 
let foo = () => 1 + 2;

So see how easy the concept is and how it can help you manage your async actions...

That's something you can live without it, but remember in programming there are always better, neater and proper ways to do things...


الإجابة المختصرة : تبدو وكأنها مقاربة معقولة تمامًا لمشكلة عدم التقارب. مع اثنين من التحذيرات.

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

انتهى بي الأمر مع اتباع نهج مماثل حقا إلى ما لديك هناك في مكتبة أخرجت من مشروعنا ، والتي أطلقنا عليها اسم react-redux-controller .

انتهى بي الأمر عدم اتباع النهج الدقيق لديك أعلاه لسببين:

  1. الطريقة التي كتبت بها ، لا تملك وظائف التوزيع هذه إمكانية الوصول إلى المتجر. يمكنك الحصول على بعض الشيء من خلال امتلاك مكونات واجهة المستخدم الخاصة بك في كل المعلومات التي تحتاجها وظيفة الإرسال. لكنني أزعم أن هذا يربط بين مكونات واجهة المستخدم هذه لمنطق الإرسال دون داعٍ. وأكثر إشكالية ، ليس هناك طريقة واضحة لوظيفة الإرسال للوصول إلى الحالة المحدثة في الاستمرالات غير المتزامنة.
  2. وظائف الإرسال لديها إمكانية dispatch نفسها عبر نطاق معجمية. هذا يحد من خيارات إعادة البناء بمجرد أن يخرج بيان connect عن السيطرة - وهو يبدو غير عملي مع طريقة update الوحيدة. لذا فأنت بحاجة إلى نظام يسمح لك بتأليف وظائف المرسل إذا قمت بتقسيمها إلى وحدات منفصلة.

خذ معا ، لديك لتزوير بعض النظام للسماح dispatch والتخزين ليتم حقنها في وظائف إيفاد الخاص بك ، جنبا إلى جنب مع المعلمات من هذا الحدث. أعرف ثلاثة أساليب معقولة لحقن التبعية:

  • redux-thunk يفعل ذلك بطريقة وظيفية ، عن طريق تمريرها إلى thunks (مما يجعلها لا thunks بالضبط على الإطلاق ، من خلال تعريفات القبة). لم أكن قد عملت مع طرق النشر الوسيطة الأخرى ، لكنني أفترض أنها في الأساس هي نفسها.
  • رد فعل رد فعل وحدة تحكم يفعل هذا مع coroutine. كمكافأة ، فإنه يمنحك أيضًا الوصول إلى "المحددات" ، وهي الوظائف التي ربما تكون قد مرت عليها كوسيطة أول connect ، بدلاً من الاضطرار إلى العمل مباشرة مع المتجر الخام ، أو المعتاد.
  • يمكنك أيضًا أن تفعل ذلك بالطريقة الشيئية عن طريق إدخالها في this السياق ، من خلال مجموعة متنوعة من الآليات الممكنة.

تحديث

يحدث لي أن هذا الجزء من اللغز هو الحد من react-redux . تحصل الوسيطة الأولى connect على لقطة حالة ، ولكن لا يتم إرسالها. يتم إرسال الحجة الثانية ولكن ليس الدولة. لا الحجة يحصل thunk أن إغلاق أكثر من الحالة الحالية ، لكونه قادراً على رؤية حالة محدثة في وقت استمرار / رد الاتصال.


لاستخدام Redux-saga هو أفضل وسيط في تطبيق React-redux.

مثال: store.js

  import createSagaMiddleware from 'redux-saga';
  import { createStore, applyMiddleware } from 'redux';
  import allReducer from '../reducer/allReducer';
  import rootSaga from '../saga';

  const sagaMiddleware = createSagaMiddleware();
  const store = createStore(
     allReducer,
     applyMiddleware(sagaMiddleware)
   )

   sagaMiddleware.run(rootSaga);

 export default store;

ثم saga.js

import {takeLatest,delay} from 'redux-saga';
import {call, put, take, select} from 'redux-saga/effects';
import { push } from 'react-router-redux';
import data from './data.json';

export function* updateLesson(){
   try{
       yield put({type:'INITIAL_DATA',payload:data}) // initial data from json
       yield* takeLatest('UPDATE_DETAIL',updateDetail) // listen to your action.js 
   }
   catch(e){
      console.log("error",e)
     }
  }

export function* updateDetail(action) {
  try{
       //To write store update details
   }  
    catch(e){
       console.log("error",e)
    } 
 }

export default function* rootSaga(){
    yield [
        updateLesson()
       ]
    }

ثم action.js

 export default function updateFruit(props,fruit) {
    return (
       {
         type:"UPDATE_DETAIL",
         payload:fruit,
         props:props
       }
     )
  }

ثم reducer.js

import {combineReducers} from 'redux';

const fetchInitialData = (state=[],action) => {
    switch(action.type){
      case "INITIAL_DATA":
          return ({type:action.type, payload:action.payload});
          break;
      }
     return state;
  }
 const updateDetailsData = (state=[],action) => {
    switch(action.type){
      case "INITIAL_DATA":
          return ({type:action.type, payload:action.payload});
          break;
      }
     return state;
  }
const allReducers =combineReducers({
   data:fetchInitialData,
   updateDetailsData
 })
export default allReducers; 

ثم main.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './app/components/App.jsx';
import {Provider} from 'react-redux';
import store from './app/store';
import createRoutes from './app/routes';

const initialState = {};
const store = configureStore(initialState, browserHistory);

ReactDOM.render(
       <Provider store={store}>
          <App />  /*is your Component*/
       </Provider>, 
document.getElementById('app'));

جرب هذا .. يعمل


ما هو الخطأ في هذا النهج؟ لماذا أرغب في استخدام Redux Thunk أو Redux Promise ، كما تقترح الوثائق؟

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

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

تعطيك الوسيطة مثل Redux Thunk أو Redux Promise "سكّر الجملة" لإرسال قطع أو وعود ، لكنك لست مضطرًا لاستخدامها.

لذلك ، بدون أي وسيط ، قد يبدو مبدع الإجراءات الخاص بك

// action creator
function loadData(dispatch, userId) { // needs to dispatch, so it is first argument
  return fetch(`http://data.com/${userId}`)
    .then(res => res.json())
    .then(
      data => dispatch({ type: 'LOAD_DATA_SUCCESS', data }),
      err => dispatch({ type: 'LOAD_DATA_FAILURE', err })
    );
}

// component
componentWillMount() {
  loadData(this.props.dispatch, this.props.userId); // don't forget to pass dispatch
}

ولكن مع Thunk Middleware ، يمكنك كتابتها على النحو التالي:

// action creator
function loadData(userId) {
  return dispatch => fetch(`http://data.com/${userId}`) // Redux Thunk handles these
    .then(res => res.json())
    .then(
      data => dispatch({ type: 'LOAD_DATA_SUCCESS', data }),
      err => dispatch({ type: 'LOAD_DATA_FAILURE', err })
    );
}

// component
componentWillMount() {
  this.props.dispatch(loadData(this.props.userId)); // dispatch like you usually do
}

لذلك لا يوجد فرق كبير. شيء واحد يعجبني في الأسلوب الأخير هو أن المكوِّن لا يهتم بأن منشئ الحدث متزامن. إنه يستدعي dispatch طبيعي فقط ، ويمكنه أيضًا استخدام mapDispatchToProps لربط مُنشئ الإجراء هذا mapDispatchToProps الجملة القصيرة ، إلخ. لا تعرف المكونات كيفية تنفيذ منشئي الإجراءات ، ويمكنك التبديل بين طرق مختلفة للتزامن (Redux Thunk، Redux Promise، Redux Saga) دون تغيير المكونات. من ناحية أخرى ، مع النهج السابق الواضح ، فإن مكوناتك تعرف تمامًا أن الاتصال المحدد هو متزامن ، ويحتاج إلى dispatch ليتم تمريره من خلال بعض الاتفاقيات (على سبيل المثال ، كمعلمة مزامنة).

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

من خلال النهج الأول ، يجب أن نضع في اعتبارنا نوع مبدع العمل الذي ندعوه:

// action creators
function loadSomeData(dispatch, userId) {
  return fetch(`http://data.com/${userId}`)
    .then(res => res.json())
    .then(
      data => dispatch({ type: 'LOAD_SOME_DATA_SUCCESS', data }),
      err => dispatch({ type: 'LOAD_SOME_DATA_FAILURE', err })
    );
}
function loadOtherData(dispatch, userId) {
  return fetch(`http://data.com/${userId}`)
    .then(res => res.json())
    .then(
      data => dispatch({ type: 'LOAD_OTHER_DATA_SUCCESS', data }),
      err => dispatch({ type: 'LOAD_OTHER_DATA_FAILURE', err })
    );
}
function loadAllData(dispatch, userId) {
  return Promise.all(
    loadSomeData(dispatch, userId), // pass dispatch first: it's async
    loadOtherData(dispatch, userId) // pass dispatch first: it's async
  );
}


// component
componentWillMount() {
  loadAllData(this.props.dispatch, this.props.userId); // pass dispatch first
}

مع منشئي عمل Redux Thunk يمكنهم dispatch نتيجة منشئي أحداث آخرين ولا يفكرون حتى في ما إذا كانت تلك متزامنة أو غير متزامنة:

// action creators
function loadSomeData(userId) {
  return dispatch => fetch(`http://data.com/${userId}`)
    .then(res => res.json())
    .then(
      data => dispatch({ type: 'LOAD_SOME_DATA_SUCCESS', data }),
      err => dispatch({ type: 'LOAD_SOME_DATA_FAILURE', err })
    );
}
function loadOtherData(userId) {
  return dispatch => fetch(`http://data.com/${userId}`)
    .then(res => res.json())
    .then(
      data => dispatch({ type: 'LOAD_OTHER_DATA_SUCCESS', data }),
      err => dispatch({ type: 'LOAD_OTHER_DATA_FAILURE', err })
    );
}
function loadAllData(userId) {
  return dispatch => Promise.all(
    dispatch(loadSomeData(userId)), // just dispatch normally!
    dispatch(loadOtherData(userId)) // just dispatch normally!
  );
}


// component
componentWillMount() {
  this.props.dispatch(loadAllData(this.props.userId)); // just dispatch normally!
}

باستخدام هذا الأسلوب ، إذا أردت لاحقًا أن يبحث getState الإجراءات في حالة Redux الحالية ، فيمكنك استخدام الوسيطة getState الثانية التي تم تمريرها إلى thunks دون تعديل رمز الاتصال على الإطلاق:

function loadSomeData(userId) {
  // Thanks to Redux Thunk I can use getState() here without changing callers
  return (dispatch, getState) => {
    if (getState().data[userId].isLoaded) {
      return Promise.resolve();
    }

    fetch(`http://data.com/${userId}`)
      .then(res => res.json())
      .then(
        data => dispatch({ type: 'LOAD_SOME_DATA_SUCCESS', data }),
        err => dispatch({ type: 'LOAD_SOME_DATA_FAILURE', err })
      );
  }
}

إذا كنت بحاجة إلى تغييره ليكون متزامنًا ، فيمكنك أيضًا القيام بذلك دون تغيير أي رمز اتصال:

// I can change it to be a regular action creator without touching callers
function loadSomeData(userId) {
  return {
    type: 'LOAD_SOME_DATA_SUCCESS',
    data: localStorage.getItem('my-data')
  }
}

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

وأخيرًا ، يعد Redux Thunk والأصدقاء مجرد طريقة واحدة ممكنة للطلبات غير المتزامنة في تطبيقات Redux. هناك طريقة أخرى مثيرة للاهتمام تتمثل في Redux Saga ، والتي تتيح لك تحديد برامج شيفرة طويلة المدى ("sagas") التي تتخذ الإجراءات فور حدوثها ، وتقوم بتحويل أو تنفيذ الطلبات قبل إخراج الإجراءات. هذا ينقل المنطق من المبدعين في العمل إلى sagas. قد ترغب في التحقق من ذلك ، واختيار ما يناسبك في وقت لاحق.

بحثت في ريبو ريكس عن القرائن ، ووجدت أن "منشئو الأعمال" مطالبون بأن يكونوا وظائف نقية في الماضي.

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


إن هدف أبراموف - ومن وجهة نظر الجميع - هو ببساطة لتغليف التعقيد (والمكالمات غير المتزامنة) في المكان المناسب .

أين هو أفضل مكان للقيام بذلك في البيانات القياسية من Redux؟ كيف حول:

  • علب التروس ؟ لا يمكن. يجب أن تكون وظائف نقية بدون أي آثار جانبية. تحديث المتجر هو عمل خطير ومعقد. لا تلوثها.
  • مكونات العرض البكم؟ بالتأكيد لا. لديهم قلق واحد: العرض التقديمي وتفاعل المستخدم ، وينبغي أن يكون بسيطا قدر الإمكان.
  • مكونات الحاوية؟ ممكن ، ولكن دون المستوى الأمثل. من المنطقي أن تكون الحاوية مكانًا نسطر فيه بعض التعقيدات المرتبطة بالتفاعل وتتفاعل مع المتجر ، ولكن:
    • تحتاج الحاويات إلى أن تكون أكثر تعقيدًا من المكونات الغبية ، ولكنها تظل مسئولية واحدة: توفير الارتباطات بين العرض والدولة / المتجر. منطقك غير المتزامن هو مصدر قلق منفصل تمامًا عن ذلك.
    • بوضعه في حاوية ، سيتم قفل منطق متزامن في سياق واحد ، لطريقة عرض واحدة / مسار واحد. فكرة سيئة. من الناحية المثالية انها كلها قابلة لإعادة الاستخدام ، وفكها تماما.
  • S ome وحدة خدمة أخرى؟ فكرة سيئة: ستحتاج إلى حقن الوصول إلى المتجر ، وهو كابوس الصيانة / القابلية للاختبار. من الأفضل الذهاب مع حبوب Redux والوصول إلى المتجر فقط باستخدام واجهات برمجة التطبيقات / النماذج المقدمة.
  • الإجراءات و Interwar التي تفسر لهم؟ لما لا؟! بالنسبة للمبتدئين ، هذا هو الخيار الرئيسي الوحيد المتبقي لدينا. :-) أكثر منطقية ، هو فصل نظام التنفيذ المنطق التنفيذ الذي يمكنك استخدامه من أي مكان. يتم الوصول إلى المتجر ويمكنه إرسال المزيد من الإجراءات. لديه مسؤولية واحدة وهي تنظيم تدفق السيطرة والبيانات حول التطبيق ، ومعظمها غير ملائم يلائم ذلك.
    • ماذا عن المبدعين العمل؟ لماذا لا تقوم بالتزامن فقط ، بدلاً من القيام بالأفعال نفسها ، وفي البرامج الوسيطة؟
      • أولاً والأكثر أهمية ، لا يمتلك منشئو المحتوى حق الوصول إلى المتجر ، كما يفعل الوسيطة. وهذا يعني أنه لا يمكنك إرسال إجراءات طارئة جديدة ، ولا يمكن قراءتها من المتجر لإنشاء برنامج التزامن الخاص بك ، إلخ.
      • لذا ، حافظ على التعقيد في مكان معقد من الضرورات ، واحتفظ بكل شيء آخر بسيطًا. يمكن للمبدعين بعد ذلك أن يكونوا وظائف بسيطة نقية وسهلة الاختبار.

نظرًا لأن العلامة <script> معفاة من "سياسة الأصل نفسه" التي تعد ضرورة أمنية في عالم الويب ، while(1) عند إضافتها إلى استجابة JSON تمنع إساءة استخدامها في العلامة <script> .





javascript asynchronous reactjs redux redux-thunk