2016-01-30 5 views
0

Я наткнулся на эту проблему недавно, и через некоторое время я не мог найти ответ, который удовлетворяет этому варианту использования, в частности.Javascript переписывает область обратного вызова

Я пытаюсь добиться следующего поведения в JavaScript

// Lets assume we have some variable defined in global scope 
var a = {val: 0} 

// What I want here is a function that sets a.val = newVal 
// and then calls the callback. 
var start = function(newVal, cb) { 
    ??? 
} 

// such that 
start(1, function() { 
    setTimeout(function() { 
    console.log(a.val) // 1 
    }, 1000) 
}) 

// and 
start(2,function() { 
    console.log(a.val) // 2 
}) 

// but in the original scope 
console.log(a.val) // 0 

Другими словами я ищу способ «завернуть» обратного вызова в другой глобальной области видимости. Я знаю, что вы можете сделать что-то подобное, обмениваясь окружающей средой или используя это; но такие методы всегда заставляют функции обратного вызова для обозначения окружающей среды в явном виде, превращая код обратного вызова в нечто вроде

start(2,function() { 
    console.log(env.a.val) // 2 
}) 

Я специально искал решение, которое сохраняет возможность использовать глобальную ссылку непосредственно из обратного вызова начала.

Не стесняйтесь использовать любую ES6/ES7 функцию, которая может каким-то образом быть смонтирована или совместима с узлом, это не предназначено для производственного кода просто весело.

EDIT: Я объясню общую проблему, поскольку многие люди предположили, что это решение может и не быть тем, что я действительно ищу.

Недавно я узнал о STM (https://wiki.haskell.org/Software_transactional_memory) и хотел поиграть с подобной идеей в js. Конечно, js работает на одном потоке, но идея заключалась в том, чтобы обеспечить одинаковый уровень изоляции для разных обратных вызовов, работающих в атомных блоках.

Пользователь имеет некоторую общую транзакционную переменную. Операции над этой переменной должны быть обернуты атомарными блоками. Что происходит под капотом, так это то, что операции в атомарном блоке выполняются не на реальном TVar, а на каком-то MockTVar, который просто записывает все записи и записи в журнале. Когда вы вызываете выполненный, журнал проверяется, соответствуют ли выполняемые операции текущему состоянию телевизоров; если это обновления, которые теперь выполняются на реальных ТВАРАх, и мы закончили (это называется фиксацией). Если это не журнал, отбрасывается и обратный вызов запускается снова. Это небольшой пример кода

var x = new TVar(2) 

// this is process a 
process.nextTick(function() { 
    atomically(x, function(x, done) { 
    a = x.readTVar() 

    setTimeout(function() { 
     x.writeTVar(a+1) 
     console.log('Process a increased, x = ', x.readTVar()) 
     done() 
    }, 2000) 
    }) 
}) 

// this is process b 
process.nextTick(function() { 
    atomically(x, function(x, done) { 
    var a = x.readTVar() 
    x.writeTVar(a+1) 
    console.log('Process b increased, x = ', x.readTVar()) 
    done() 
}) 

})

В этом примере процессе а будет пытаться совершить, но так как процесс б изменил значение х (и совершенным, что изменение перед) фиксацией воли fail, и обратный вызов будет запущен еще раз.

Как вы можете видеть, я возвращаю mockTVars в обратном вызове, но я считаю это немного уродливым по двум причинам: 1) Если вы хотите заблокировать более одной переменной (и, как правило, вы делаете), у меня нет выбора но возвращать массив mockTVars, заставляя пользователя извлекать их один за другим, если он хочет использовать их в чистоте. 2) Пользователь должен убедиться, что имя mockTVar, которое передано обратному вызову, соответствует имени реального TVar, если он хочет иметь возможность рассуждать о том, что происходит, не теряя рассудка. То, что я имею в виду, что в этой строке

atomically(x, function(x, done) {..}) 

Это до пользователя, чтобы использовать то же имя для обозначения как к фактическому TVAR и издевались TVAR (имя х в данном примере).

Надеюсь, это объяснение поможет.Спасибо всем, кто нашел время, чтобы помочь мне

+3

Почему вы пытаетесь сделать это? убедитесь, что это не проблема [xy] (http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) – jeremy

+0

Я думаю, что этот тип вещей возможен только в команде lisp с макросами или в Emacs lisp с динамическими переменными. – jcubic

+0

Вы, по сути, спрашиваете, как использовать одну переменную для хранения нескольких значений, что невозможно. – michael

ответ

1

Я все еще хочу, чтобы вы описали фактическую проблему, которую пытаетесь решить, но вот одна идея, которая делает копию глобального объекта, передает его в обратный вызов и обратный вызов может использовать то же имя, что и глобальное, и тогда он будет «переопределять» доступ к глобальному только для этой области.

var a = {val: 0, otherVal: "hello"} ; 
 
    
 
    function start(newVal, cb) { 
 
     var copy = {}; 
 
     Object.assign(copy, a); 
 
     copy.val = newVal; 
 
     cb(copy); 
 
    } 
 
    
 
    log("Before start, a.val = " + a.val); 
 
    start(1, function(a) { 
 
     // locally scoped copy of "a" here that is different than the global "a" 
 
     log("Beginning of start, a.val = " + a.val) // 1 
 
     a.val = 2; 
 
     log("End of start, a.val = " + a.val) // 2 
 
    }); 
 

 
    log("After start, a.val = " + a.val); 
 
    
 
    function log(x) { 
 
     document.write(x + "<br>"); 
 
    }

+0

Спасибо, но это не то, что я действительно ищу, я специально стараюсь избегать передачи переменной в обратном вызове. Я действительно нашел vooeery уродливое решение, которое состоит в установке значения на a, а затем вызове 'toString()' на обратном вызове, а затем вызывая его в функции запуска. Это в основном заставляет его работать так, как будто обратный вызов был определен внутри функции запуска, предоставляя ему требуемую область видимости. Это работает, но это немного похоже на то, что лечение в первую очередь хуже, чем болезнь: P – nazrhom

+0

@ user2512250 - Вы не можете изменить определение 'a' в области без изменения' a' или определения нового в локальный охват. Это только правила Javascript. Если вы исключаете оба этих варианта, тогда неясно, о чем вы просите. Это ваши варианты за пределами какого-то действительно грубого взлома, как попытки динамического изменения кода. – jfriend00

+0

Я не уверен, что понимаю, что вы имеете в виду. Проблема, с которой я столкнулась, заключается в том, что вы вызываете 'toString' функцию, а затем вызываете eval в этой строке, а не касаетесь значения' a'. То, что я пытаюсь исключить, - это необходимость передачи измененного значения в обратном вызове. – nazrhom

Смежные вопросы