RoundtableJS uses JavaScript’s async/await syntax for handling asynchronous operations. Here’s a basic example:

async function runSurvey() {
    const survey = new Survey({ participantId: 'participant_123' });
    
    // This line waits for the page to be shown and answered
    await survey.showPage({ id: 'page1', elements: [question1] });
    
    // This code runs only after page1 is completed
    console.log('Page 1 completed');
    
    // Show the next page
    await survey.showPage({ id: 'page2', elements: [question2] });
    
    survey.finishSurvey();
}

// Start the survey
runSurvey();

In this example, await pauses execution until each page is completed, allowing for a linear programming schema that represents the survey flow.

Why Async for Surveys?

Surveys involve multiple time-consuming operations:

  • Waiting for user input
  • Fetching data from a server
  • Performing calculations for branching logic

Basic Logic

Conditional Logic: Show or hide questions based on previous responses

async function runSurvey() {
    const survey = new Survey({ participantId: 'participant_123' });
    
    const q1 = new SingleSelect({
        id: 'pet_preference',
        text: 'Which pet do you prefer?',
        options: ['Dog', 'Cat', 'Neither']
    });
    
    await survey.showPage({ id: 'page1', elements: [q1] });
    
    // Conditional logic: Only show follow-up question if 'Neither' wasn't selected
    if (survey.getResponse('pet_preference') !== 'Neither') {
        const q2 = new OpenEnd({
            id: 'pet_reason',
            text: `Why do you prefer ${survey.getResponse('pet_preference')}s?`
        });
        await survey.showPage({ id: 'page2', elements: [q2] });
    }
    
    survey.finishSurvey();
}

Skip Logic: Skip entire pages based on responses

async function runSurvey() {
    const survey = new Survey({ participantId: 'participant_123' });
    
    // First page with age question
    const ageQuestion = new NumberEntry({
        id: 'age',
        text: 'What is your age?',
        min: 1,
        max: 120
    });
    await survey.showPage({ id: 'age_page', elements: [ageQuestion] });
    
    // Skip logic based on age
    if (survey.getResponse('age') >= 18) {
        await showAdultQuestions(survey);
    } else {
        await showMinorQuestions(survey);
    }
    
    survey.finishSurvey();
}

async function showAdultQuestions(survey) {
    // Implementation of adult-specific questions
}

async function showMinorQuestions(survey) {
    // Implementation of minor-specific questions
}

Piping: Use previous responses in subsequent questions

async function runSurvey() {
    const survey = new Survey({ participantId: 'participant_123' });
    
    // First question about pet preference
    const q1 = new SingleSelect({
        id: 'pet_preference',
        text: 'Which pet do you prefer?',
        options: ['Dog', 'Cat', 'Fish']
    });
    await survey.showPage({ id: 'page1', elements: [q1] });
    
    // Piping the response into the next question
    const favoritePet = survey.getResponse('pet_preference');
    const q2 = new OpenEnd({
        id: 'pet_description',
        text: `Describe your ideal ${favoritePet}.`
    });
    await survey.showPage({ id: 'page2', elements: [q2] });
    
    survey.finishSurvey();
}

Advanced Logic

Complex Branching: Set branching criteria based on multiple conditions

async function runSurvey() {
    const survey = new Survey({ participantId: 'participant_123' });
    
    // Collect demographic information
    await collectDemographics(survey);
    
    const age = survey.getResponse('age');
    const income = survey.getResponse('income');
    const education = survey.getResponse('education');
    
    // Complex branching based on multiple criteria
    if (age > 30 && income > 50000 && education === 'Bachelor\'s or higher') {
        await showHighValueCustomerQuestions(survey);
    } else if (age <= 30 && income <= 30000) {
        await showYoungBudgetCustomerQuestions(survey);
    } else {
        await showGeneralCustomerQuestions(survey);
    }
    
    survey.finishSurvey();
}

async function collectDemographics(survey) {
    // Implementation of demographic questions
}

async function showHighValueCustomerQuestions(survey) {
    // Questions for high-value customers
}

async function showYoungBudgetCustomerQuestions(survey) {
    // Questions for young budget-conscious customers
}

async function showGeneralCustomerQuestions(survey) {
    // General customer questions
}

Looping: Circle through repetitive question sets for multiple options

async function runSurvey() {
    const survey = new Survey({ participantId: 'participant_123' });
    
    const numberOfProducts = await askNumberOfProducts(survey);
    
    // Loop through product questions
    for (let i = 0; i < numberOfProducts; i++) {
        await askProductQuestions(survey, i + 1);
    }
    
    survey.finishSurvey();
}

async function askNumberOfProducts(survey) {
    const q = new NumberEntry({
        id: 'num_products',
        text: 'How many products do you want to rate?',
        min: 1,
        max: 5
    });
    
    await survey.showPage({ id: 'num_products_page', elements: [q] });
    
    return survey.getResponse('num_products');
}

async function askProductQuestions(survey, productNumber) {
    const q1 = new SingleSelect({
        id: `product_${productNumber}_rating`,
        text: `How would you rate product ${productNumber}?`,
        options: ['Poor', 'Fair', 'Good', 'Excellent']
    });
    
    const q2 = new OpenEnd({
        id: `product_${productNumber}_feedback`,
        text: `Any additional feedback for product ${productNumber}?`
    });
    
    await survey.showPage({ id: `product_${productNumber}_page`, elements: [q1, q2] });
}

Quota Management: Implement quotas to limit responses based on certain criteria

async function checkAndUpdateQuota(age, gender) {
    // This function would typically involve a server call to check and update quotas
    // For this example, we'll simulate it with a local check
    const quotaFilled = await simulateQuotaCheck(age, gender);
    if (quotaFilled) {
        throw new Error('QUOTA_FILLED');
    }
}

async function simulateQuotaCheck(age, gender) {
    // Simulate a server call with a random result
    await new Promise(resolve => setTimeout(resolve, 1000)); // Simulate network delay
    return Math.random() < 0.2; // 20% chance the quota is filled
}

async function conductMainSurvey(survey) {
    // Implementation of the main survey questions
}

async function runSurvey() {
    const survey = new Survey({ participantId: 'participant_123' });
    
    // Collect demographic information
    await collectDemographics(survey);
    
    const age = survey.getResponse('age');
    const gender = survey.getResponse('gender');
    
    try {
        // Check if the quota for this demographic is filled
        await checkAndUpdateQuota(age, gender);
    } catch (error) {
        if (error.message === 'QUOTA_FILLED') {
            survey.finishSurvey({ message: 'Thank you, but our quota for your demographic has been filled.' });
            return;
        }
        // Handle other errors
        console.error('Error checking quota:', error);
        survey.finishSurvey({ error: 'An unexpected error occurred.' });
        return;
    }
    
    // Continue with the survey if quota is not filled
    await conductMainSurvey(survey);
    
    survey.finishSurvey();
}