/**
 * Эта функция есть ключевая для работы всей хитрой схемы нашей асинхронности. Лазить руками не рекомендую дабы не сломать
 */
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator.throw(value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments)).next());
    });
};

// ==UserScript==
// @name           some Ajax script
// @namespace      virtonomica
// @author         mr_Sumkin
// @description    простой шаблон скрипта с ajax запросами к серверу
// @include        http*://virtonomic*.*/*/main/*
// @require        https://code.jquery.com/jquery-1.11.1.min.js
// @version        1.0
// ==/UserScript==

$ = jQuery = jQuery.noConflict(true);
let $xioDebug = true;

// упрощаем себе жисть, подставляем имя скрипта всегда в лог сообщении
function log(msg, ...args) {
    msg = "scriptname: " + msg; // сюда стоит вписать ваше имя скрипта
    let arr = [];
    arr.push(msg);
    arr.push.apply(arr, args);
    logDebug.apply(null, arr);
}

/**
 * Главная функция скрипта. Она тоже асинхронная.
   Любая функция внутри которой будет производиться асинхронный вызов, обязана быть тоже асинхронной
   иначе работать как положено не будет.
 */
function run_async() {
    return __awaiter(this, void 0, void 0, function* () {
        log("начали");
        // обычно толковый скрипт, сначала пытается что то отрисовать на странице из УЖЕ собранных данных
        // а потом запрашивает новые данные по сети и обновляет страничку по мере получения этих данных
        // следовательно сначала идет код отрисовки чего либо на страничке. Он обычный синхронный код
        let result = someCommonCode(document);


        // после выполнения стандартного обычного кода мы заводим различные запросы к серверу
        // Используется особый формат вызова асинхронной функции. Он нужен для того, чтобы выполнение функции 
        // не продолжалось дальше, а ожидало получения данных от запроса. Как только данные будут получены, 
        // или произойдет ошибка запроса, функция начнет выполняться дальше.
        // Если использовать обычный способ вызова без ключевого слова yield то после запуска функции
        // someAsyncCode_async код начнет выполняться дальше, так как функция someAsyncCode_async асинхронная.
        // Как итог будет ошибка, ведь в переменной asyncResult еще нет ничего и она undefined
        let asyncResult = "";

        // любой код способен давать ошибки, а асинхронный тем более, поэтому оптимально завернуть его в блок try-catch
        // смысл этого куска кода - показать как легко работать с исключениями при использовании описанного здесь подхода
        // к асинхронным запросам.
        try {
            asyncResult = yield someAsyncCode_async();
        }
        catch (err) {
            // если наша асинхронная функция вдруг была выполнена неудачно в связи с ошибками в нашем кривом коде
            // или данные не пришли от сервера, то someAsyncCode_async генерирует исключение, 
            // которое мы тут и ловим, а дальше просто выводим в консоль текст ошибки
            // либо делаем что то что нам в голову взбредет, но лучше делать что то что позволит сразу увидеть что ошибка
            // а не сидеть и гадать почему ничего не происходит
            let e = err;
            log(e.message);
        }

        // теперь зная результат запроса, мы можем обновить информацию на страничке снова используя обычную функцию
        // никто не запрещает нам и этот код завернуть в try-catch но это уже на ваше усмотрение
        someUpdateCode(document, asyncResult);
        log("закончили");
    });
}

/**
 * Синхронный обычный код
 * @param doc сюда можно подать документ, и дальше отработать его обычными методами jquery
 */
function someCommonCode(doc) {
    // сюда можно поместить весь стандартный код для удобства.
    // этот код выполнится до отправки запросов к серверу
    log("someCommonCode выполнен успешно");
    return "done";
}

/**
 * Эта фукнция обновит страничку новыми данными
 * @param doc
 * @param newData новые данные которые нужно отобразить
 */
function someUpdateCode(doc, newData) {
    // пишем сюда обычный синхронный код обновления данных на нашей страничке.
    // все как обычно
    let $html = $(doc);
    $html.find("div.artf_slots").before("<b>ДАННЫЕ ПОЛУЧЕНЫ ОТ СЕРВЕРА</b>");
    log("someUpdateCode выполнен успешно");
}

/**
 * Некая функция с асинхронным кодом. рекомендую давать суффикс async для таких, дабы не забыть о ее асинхронности
 */
function someAsyncCode_async() {

    // вот тут вся главная фишка. Внутри ЛЮБОЙ нашей асинхронной функции по факту будет будет вызыватьеся наш
    // __awaiter с 4 параметрами. Первые 3 у нас стандартные и всегда такие, а вот последний являет собой функцию
    // ту самую которую мы хотим создать. То есть тело любой функции которую мы хотим сделать асинхронной нужно просто завернуть
    // в return __awaiter(this, void 0, void 0, function* () { "здеся будет наш код"})
    // значок * после слова function указывает что это функция генератор и этот значок должен быть обязательно!!!

    // ГЛАВНОЕ правило таково: если мы делаем асинхронный запрос в теле нашей функции с помощью слова yield тогда
    // обязательно завернуть все тело функции в __awaiter
    return __awaiter(this, void 0, void 0, function* () {
        // после выполнения стандартного обычного кода мы заводим различные запросы к серверу
        let url_tpl = `https://virtonomica.ru/olga/forum/forum_new/15/view`;

        // Используется особый формат вызова асинхронной функции. Он нужен для того, чтобы выполнение функции 
        // не продолжалось дальше, а ожидало получения данных от запроса. Как только данные будут получены, 
        // или произойдет ошибка запроса, функция начнет выполняться дальше.
        // Если использовать обычный способ вызова без ключевого слова yield то после запуска функции
        // tryGet_async код начнет выполняться дальше, так как функция tryGet_async асинхронная.
        // Как итог будет ошибка, ведь в переменной html еще нет ничего и она undefined
        let obj = yield tryGet_async(url_tpl);

        // так как функция tryGet_async возвращает объект Error в случае если запрос неудачный
        // мы легко можем это обработать
        if (obj instanceof Error) {
            log("someAsyncCode_async выполнен c ошибкой");
            throw obj; // просто выбрасываем исключение если нам проще работать с исключениями
        }

        // здесь уже данные точно получены, можем их парсить и насиловать как нам угодно
        // НО здесь не стоит изменять исходную страницу
        // смысл всей функции в получении данных а не в изменении данных на страницах сайта
        let $html = $(obj);
        let $tbl = $html.find("table.list");
        // ...
        // ...

        // когда данные отработаны и все нужные величины получены, завершаем функцию и возвращаем из функции эти величины
        log("someAsyncCode_async выполнен успешно");
        return "async done";
    });
}


// собственно сам запуск скрипта в работу
// хоть мы и вызываем асинхронную функцию, но вызываем ее как обычную, тем самым страничка 
// на которой запускается данный скрипт не замораживается на время ожидания ответов от запросов к серверу 
$(document).ready(() => run_async());



//
// Набор штатных функций для стандартной обработки запросов
// 
/**
 * Запрашивает страницу. При ошибке поробует повторить запрос через заданное число секунд.
 * Пробует заданное число попыток, после чего возвращает reject.
 * При ресолве вернет текст страницы, а при реджекте вернет Error объект
 * @param url
 * @param retries число попыток загрузки
 * @param timeout таймаут между попытками
 */
function tryGet_async(url, retries = 10, timeout = 1000) {
    return __awaiter(this, void 0, void 0, function* () {
        let $deffered = $.Deferred();
        $.ajax({
            url: url,
            type: "GET",
            success: (data, status, jqXHR) => $deffered.resolve(data),
            error: function (jqXHR, textStatus, errorThrown) {
                retries--;
                if (retries <= 0) {
                    let err = new Error(`can't get ${this.url}\nstatus: ${jqXHR.status}\ntextStatus: ${jqXHR.statusText}\nerror: ${errorThrown}`);
                    $deffered.reject(err);
                    return;
                }
                let _this = this;
                setTimeout(() => { $.ajax(_this); }, timeout);
            }
        });
        return $deffered.promise();
    });
}

/**
 * Берет строку JSON и конвертает поля в данные. Числа в числа, null в нулл, и t/f в true/false
 * @param jsonStr
 */
function parseJSON(jsonStr) {
    let obj = JSON.parse(jsonStr, (k, v) => {
        if (v === "t")
            return true;
        if (v === "f")
            return false;
        return (typeof v === "object" || isNaN(v)) ? v : parseFloat(v);
    });
    return obj;
}

/**
 * Аналогично обычному методу tryGet_async правда возвращает json объект
   и конвертает по ходу дела числа в числа если они идут строкой
 */
function tryGetJSON_async(url, retries = 10, timeout = 1000) {
    return __awaiter(this, void 0, void 0, function* () {
        let $deffered = $.Deferred();
        $.ajax({
            url: url,
            type: "GET",
            cache: false,
            dataType: "text",
            success: (jsonStr, status, jqXHR) => {
                let obj = parseJSON(jsonStr);
                $deffered.resolve(obj);
            },
            error: function (jqXHR, textStatus, errorThrown) {
                retries--;
                if (retries <= 0) {
                    let err = new Error(`can't get ${this.url}\nstatus: ${jqXHR.status}\ntextStatus: ${jqXHR.statusText}\nerror: ${errorThrown}`);
                    $deffered.reject(err);
                    return;
                }
                let _this = this;
                setTimeout(() => { $.ajax(_this); }, timeout);
            }
        });
        return $deffered.promise();
    });
}

function logDebug(msg, ...args) {
    if (!$xioDebug)
        return;
    console.log(msg, ...args);
}

Add a code snippet to your website: www.paste.org