{"name":"Script Handle","title":"Script Handle","type":"topic","url":"https://community.bistudio.com/wiki/Script_Handle","revid":378908,"updated":"2026-08-11T15:02:59Z","aliases":["Script (Handle)"],"markdown":"A **Script Handle**, used to identify scripts in script operations called using spawn or execVM.\nIntroduced with Armed Assault, does not refer to Operation Flashpoint or Operation Flashpoint: Resistance.\n\nThis handle's status can be checked with scriptDone, it can be terminated with terminate.\n\n> [!NOTE]\n> **Arma 3:** In Arma 3:\n> - the handle's status can also be checked with isNull (A completed script will become null)\n> - the handle can be accessed from whithin itself using `_thisScript` (since Arma 3 v1.54)\n\n> [!NOTE]\n> For more information about scripts and scripting, see Scripting Topics and Introduction to Arma Scripting.\n\n### Promise Handles (Arma 3 v2.22)\n\nScript handles can be used as [promises](https://en.wikipedia.org/wiki/Futures_and_promises).\n\nA Promise is either a Script Handle to a spawned or execVMed script, or an \"Empty Handle\" that does not hold a backing script.\nThe Handle can be used to wait for the a result to be available.\nIn case of a \"real\" handle that means the scheduled script returning a value, or in the case of a \"Empty Handle\" by using terminate to set a result value.\n\nA Promise can have continuations added onto it with continueWith, which is script code that will execute once the handle is completed.\n\nHandles can be used with waitUntil, continueWith, terminate, scriptDone, name.\n\n#### Creating a Promise\n\nEvery handle returned by spawn or execVM is a promise that gets resolved once the script exits.\n\nIt's also possible to create a \"Empty Handle\" which does not hold any scheduled script, and can also be used in unscheduled code.\n\nA \"Empty Handle\" needs to be \"completed\" by calling terminate on it.\n\n```sqf\nprivate _emptyHandle = spawn \"HandleName\"; // does not contain an actual script, it's only a holder for name and potentially a value\nprivate _normalHandle = 0 spawn { scriptName \"HandleName\"; }; // contains an actual scheduled script that will execute and return a value at the end\n```\n\n#### Using Promises\n\nPromises can be used to simplify asynchronous operations.\nFor example, when calling a function that takes some time to complete, and then wanting to execute code after it has completed.\n\nTo show it on an example, let's say we have a function that paradrops a vehicle.\nAnd we want to wait for the vehicle to touch the ground and then spawn a smoke grenade on it.\n\nPreviously, that could be solved like so:\n\n```sqf\nfnc_StartParadrop = {\n    params [\"_object\"];\n\n    private _parachute = createVehicle [\"B_Parachute_02_F\", [0, 0, 0], [], 0, \"CAN_COLLIDE\"];\n\n    // Put the parachute into the right position (setPos doesn't work on them)\n    _parachute attachTo [_object, [0, 0, 0]];\n    detach _parachute;\n\n    _object attachTo [_parachute, [0, 0, 0]];\n    // Now the object is hanging on parachute and gliding down\n    // But we want to wait for it to arrive at the bottom, the parachute will be deleted once it lands, so we can use that\n\n    // The parachute won't know about us, and we cannot get a value out of the eventhandler, so we will give the parachute a place to store the result\n    // Note this will not work across savegame save/load because the \"result\" reference will be lost.\n\n    private _result = [];\n    _parachute setVariable [\"result\", _result];\n    _parachute addEventHandler [\"Deleted\", {\n        params [\"_entity\"];\n        (_entity getVariable \"result\") set [0, getPos _entity]; // Store the landing position of the parachute\n    }];\n\n    // _result will be set once the parachute landed. How we wait on the result now depends on whether this is a scheduled or unscheduled function\n    // In unscheduled code we cannot wait, we would need to get some callback function in our arguments to notify our caller that it can continue.\n    // For simplicity we show the scheduled variant here, the unscheduled would be more complex\n\n    // Keep looping, until someone (Hopefully the Deleted eventhandler) wrote a result into our array\n    waitUntil { _result isNotEqualTo []; };\n\n    _result select 0; // We return the result, our user must have call'ed us to wait for us to finish and get the result\n};\n\n// The fnc_StartParadrop will wait, so we have to spawn a new script to not hold up everything else\n0 spawn {\n    private _dropPos = ParadropVehicle call fnc_StartParadrop; // This will wait until the vehicle has landed\n    \"SmokeshellGreen\" createVehicle _dropPos; // Spawn a smoke at the landing position\n};\n```\n\nAlso note how the waitUntil always runs.\nEven if we as the caller do not care for the paradrop to finish, we wouldn't easily have the option to tell the script that, without adding another parameter to it.\nAlso note that this function is not usable in unscheduled scripts, because it uses waitUntil to wait for the landing.\n\nWith promises, it can be solved like this:\n\n```sqf\nfnc_StartParadrop = {\n    params [\"_object\"];\n\n    private _parachute = createVehicle [\"B_Parachute_02_F\", [0, 0, 0], [], 0, \"CAN_COLLIDE\"];\n\n    // Put the parachute into the right position (setPos doesn't work on them)\n    _parachute attachTo [_object, [0, 0, 0]];\n    detach _parachute;\n\n    _object attachTo [_parachute, [0, 0, 0]];\n    // Now the object is hanging on parachute and gliding down\n    // But we want to wait for it to arrive at the bottom, the parachute will be deleted once it lands, so we can use that\n\n    // The parachute won't know about us, and we cannot get a value out of the eventhandler, so we will give the parachute a place to store the result\n    private _paradropHandle = spawn \"Paradrop\"; // We create new Promise, that we will use to return the result\n\n    _parachute setVariable [\"handle\", _paradropHandle];\n    _parachute addEventHandler [\"Deleted\", {\n        params [\"_entity\"];\n        (_entity getVariable \"handle\") terminate (getPos _entity); // Complete the promise and pass the landing position as result\n    }];\n    _paradropHandle // We return the promise, the caller can decide if they want to use it or not\n};\n\n// Note we do not need to spawn another script because we are not waiting for anything, this can fully be handled in unscheduled code now\n\n_paradropHandle = ParadropVehicle call fnc_StartParadrop; // This will start the paradrop, and return a handle that we can use to wait for it to land\n_paradropHandle continueWith { \"SmokeshellGreen\" createVehicle _this; }; // We use a continuation, it will be triggered once terminate is called on the handle (Inside the \"Deleted\" eventhandler)\n```\n\nNote that the paradrop function can now also be used in scheduled scripts like so:\n\n```sqf\nprivate _paradropHandle = ParadropVehicle call fnc_StartParadrop; // This will start the paradrop, and return a handle that we can use to wait for it to land\nprivate _dropPos = waitUntil _paradropHandle; // Wait for the promise to be completed and get its result\n\"SmokeshellGreen\" createVehicle _dropPos; // Spawn a smoke at the landing position\n```\n\nOr like so:\n\n```sqf\nprivate _paradropHandle = ParadropVehicle call fnc_StartParadrop; // This will start the paradrop, and return a handle that we can use to wait for it to land\nprivate _dropPos = waitUntil [_paradropHandle, 60]; // Wait for the promise to be completed, but at most for 60 seconds, if it still has not landed yet, we will report it to the player\n\nif (isNil \"_dropPos\") then {\n    // The promise yielded no result. Either it was terminated without a result, or our timeout has elapsed\n    hint \"The Paradrop has failed!\";\n} else {\n    \"SmokeshellGreen\" createVehicle _dropPos; // Spawn a smoke at the landing position\n};\n```","sections":[{"heading":null,"level":1,"markdown":"A **Script Handle**, used to identify scripts in script operations called using spawn or execVM.\nIntroduced with Armed Assault, does not refer to Operation Flashpoint or Operation Flashpoint: Resistance.\n\nThis handle's status can be checked with scriptDone, it can be terminated with terminate.\n\n> [!NOTE]\n> **Arma 3:** In Arma 3:\n> - the handle's status can also be checked with isNull (A completed script will become null)\n> - the handle can be accessed from whithin itself using `_thisScript` (since Arma 3 v1.54)\n\n> [!NOTE]\n> For more information about scripts and scripting, see Scripting Topics and Introduction to Arma Scripting."},{"heading":"Promise Handles (Arma 3 v2.22)","level":3,"markdown":"Script handles can be used as [promises](https://en.wikipedia.org/wiki/Futures_and_promises).\n\nA Promise is either a Script Handle to a spawned or execVMed script, or an \"Empty Handle\" that does not hold a backing script.\nThe Handle can be used to wait for the a result to be available.\nIn case of a \"real\" handle that means the scheduled script returning a value, or in the case of a \"Empty Handle\" by using terminate to set a result value.\n\nA Promise can have continuations added onto it with continueWith, which is script code that will execute once the handle is completed.\n\nHandles can be used with waitUntil, continueWith, terminate, scriptDone, name."},{"heading":"Creating a Promise","level":4,"markdown":"Every handle returned by spawn or execVM is a promise that gets resolved once the script exits.\n\nIt's also possible to create a \"Empty Handle\" which does not hold any scheduled script, and can also be used in unscheduled code.\n\nA \"Empty Handle\" needs to be \"completed\" by calling terminate on it.\n\n```sqf\nprivate _emptyHandle = spawn \"HandleName\"; // does not contain an actual script, it's only a holder for name and potentially a value\nprivate _normalHandle = 0 spawn { scriptName \"HandleName\"; }; // contains an actual scheduled script that will execute and return a value at the end\n```"},{"heading":"Using Promises","level":4,"markdown":"Promises can be used to simplify asynchronous operations.\nFor example, when calling a function that takes some time to complete, and then wanting to execute code after it has completed.\n\nTo show it on an example, let's say we have a function that paradrops a vehicle.\nAnd we want to wait for the vehicle to touch the ground and then spawn a smoke grenade on it.\n\nPreviously, that could be solved like so:\n\n```sqf\nfnc_StartParadrop = {\n    params [\"_object\"];\n\n    private _parachute = createVehicle [\"B_Parachute_02_F\", [0, 0, 0], [], 0, \"CAN_COLLIDE\"];\n\n    // Put the parachute into the right position (setPos doesn't work on them)\n    _parachute attachTo [_object, [0, 0, 0]];\n    detach _parachute;\n\n    _object attachTo [_parachute, [0, 0, 0]];\n    // Now the object is hanging on parachute and gliding down\n    // But we want to wait for it to arrive at the bottom, the parachute will be deleted once it lands, so we can use that\n\n    // The parachute won't know about us, and we cannot get a value out of the eventhandler, so we will give the parachute a place to store the result\n    // Note this will not work across savegame save/load because the \"result\" reference will be lost.\n\n    private _result = [];\n    _parachute setVariable [\"result\", _result];\n    _parachute addEventHandler [\"Deleted\", {\n        params [\"_entity\"];\n        (_entity getVariable \"result\") set [0, getPos _entity]; // Store the landing position of the parachute\n    }];\n\n    // _result will be set once the parachute landed. How we wait on the result now depends on whether this is a scheduled or unscheduled function\n    // In unscheduled code we cannot wait, we would need to get some callback function in our arguments to notify our caller that it can continue.\n    // For simplicity we show the scheduled variant here, the unscheduled would be more complex\n\n    // Keep looping, until someone (Hopefully the Deleted eventhandler) wrote a result into our array\n    waitUntil { _result isNotEqualTo []; };\n\n    _result select 0; // We return the result, our user must have call'ed us to wait for us to finish and get the result\n};\n\n// The fnc_StartParadrop will wait, so we have to spawn a new script to not hold up everything else\n0 spawn {\n    private _dropPos = ParadropVehicle call fnc_StartParadrop; // This will wait until the vehicle has landed\n    \"SmokeshellGreen\" createVehicle _dropPos; // Spawn a smoke at the landing position\n};\n```\n\nAlso note how the waitUntil always runs.\nEven if we as the caller do not care for the paradrop to finish, we wouldn't easily have the option to tell the script that, without adding another parameter to it.\nAlso note that this function is not usable in unscheduled scripts, because it uses waitUntil to wait for the landing.\n\nWith promises, it can be solved like this:\n\n```sqf\nfnc_StartParadrop = {\n    params [\"_object\"];\n\n    private _parachute = createVehicle [\"B_Parachute_02_F\", [0, 0, 0], [], 0, \"CAN_COLLIDE\"];\n\n    // Put the parachute into the right position (setPos doesn't work on them)\n    _parachute attachTo [_object, [0, 0, 0]];\n    detach _parachute;\n\n    _object attachTo [_parachute, [0, 0, 0]];\n    // Now the object is hanging on parachute and gliding down\n    // But we want to wait for it to arrive at the bottom, the parachute will be deleted once it lands, so we can use that\n\n    // The parachute won't know about us, and we cannot get a value out of the eventhandler, so we will give the parachute a place to store the result\n    private _paradropHandle = spawn \"Paradrop\"; // We create new Promise, that we will use to return the result\n\n    _parachute setVariable [\"handle\", _paradropHandle];\n    _parachute addEventHandler [\"Deleted\", {\n        params [\"_entity\"];\n        (_entity getVariable \"handle\") terminate (getPos _entity); // Complete the promise and pass the landing position as result\n    }];\n    _paradropHandle // We return the promise, the caller can decide if they want to use it or not\n};\n\n// Note we do not need to spawn another script because we are not waiting for anything, this can fully be handled in unscheduled code now\n\n_paradropHandle = ParadropVehicle call fnc_StartParadrop; // This will start the paradrop, and return a handle that we can use to wait for it to land\n_paradropHandle continueWith { \"SmokeshellGreen\" createVehicle _this; }; // We use a continuation, it will be triggered once terminate is called on the handle (Inside the \"Deleted\" eventhandler)\n```\n\nNote that the paradrop function can now also be used in scheduled scripts like so:\n\n```sqf\nprivate _paradropHandle = ParadropVehicle call fnc_StartParadrop; // This will start the paradrop, and return a handle that we can use to wait for it to land\nprivate _dropPos = waitUntil _paradropHandle; // Wait for the promise to be completed and get its result\n\"SmokeshellGreen\" createVehicle _dropPos; // Spawn a smoke at the landing position\n```\n\nOr like so:\n\n```sqf\nprivate _paradropHandle = ParadropVehicle call fnc_StartParadrop; // This will start the paradrop, and return a handle that we can use to wait for it to land\nprivate _dropPos = waitUntil [_paradropHandle, 60]; // Wait for the promise to be completed, but at most for 60 seconds, if it still has not landed yet, we will report it to the player\n\nif (isNil \"_dropPos\") then {\n    // The promise yielded no result. Either it was terminated without a result, or our timeout has elapsed\n    hint \"The Paradrop has failed!\";\n} else {\n    \"SmokeshellGreen\" createVehicle _dropPos; // Spawn a smoke at the landing position\n};\n```"}],"categories":["Data Types"],"groups":["Data Types"]}