Promise
首先,我们看下JS里面promise的实现,注意这里带有@符号前缀的方法有部分是由对应的C++方法实现的,可以在工程里根据方法名字搜索
先看下构造方法
@globalPrivate
function initializePromise(executor)
{
"use strict";
if (typeof executor !== 'function')
@throwTypeError("Promise constructor takes a function argument");
//设置promiseState为pending
@putByIdDirectPrivate(this, "promiseState", @promiseStatePending);
//初始化promiseReactions数组
@putByIdDirectPrivate(this, "promiseReactions", []);
@putByIdDirectPrivate(this, "promiseIsHandled", false);
//创建解决函数,也就是resolve和reject
var resolvingFunctions = @createResolvingFunctions(this);
try {
executor(resolvingFunctions.@resolve, resolvingFunctions.@reject);
} catch (error) {
return resolvingFunctions.@reject.@call(@undefined, error);
}
return this;
}
@globalPrivate
function createResolvingFunctions(promise)
{
"use strict";
var alreadyResolved = false;
function @resolve(resolution) {
if (alreadyResolved)
return @undefined;
alreadyResolved = true;
//如果resolve的函数就是promise本身,则报错
if (resolution === promise)
return @rejectPromise(promise, new @TypeError("Resolve a promise with itself"));
if (!@isObject(resolution))
return @fulfillPromise(promise, resolution);
//尝试获取then
var then;
try {
then = resolution.then;
} catch (error) {
//出现错误,则调用reject
return @rejectPromise(promise, error);
}
if (typeof then !== 'function')
return @fulfillPromise(promise, resolution);
//获取成功的话,往任务队列中追加一个promiseResolveThenableJob任务
@enqueueJob(@promiseResolveThenableJob, [promise, resolution, then]);
return @undefined;
}
function @reject(reason) {
if (alreadyResolved)
return @undefined;
alreadyResolved = true;
return @rejectPromise(promise, reason);
}
return { @resolve, @reject };
}
@globalPrivate
//这个任务的作用就是调用promise的then函数,并捕获异常
function promiseResolveThenableJob(promiseToResolve, thenable, then)
{
"use strict";
var resolvingFunctions = @createResolvingFunctions(promiseToResolve);
try {
return then.@call(thenable, resolvingFunctions.@resolve, resolvingFunctions.@reject);
} catch (error) {
return resolvingFunctions.@reject.@call(@undefined, error);
}
}
再看下fullfill和reject
@globalPrivate
function triggerPromiseReactions(state, reactions, argument)
{
"use strict";
//把所有的reactions(resolve和reject的组合)取出来放入队列执行
for (var index = 0, length = reactions.length; index < length; ++index)
@enqueueJob(@promiseReactionJob, [state, reactions[index], argument]);
}
@globalPrivate
function rejectPromise(promise, reason)
{
"use strict";
var reactions = @getByIdDirectPrivate(promise, "promiseReactions");
@putByIdDirectPrivate(promise, "promiseResult", reason);
@putByIdDirectPrivate(promise, "promiseReactions", @undefined);
@putByIdDirectPrivate(promise, "promiseState", @promiseStateRejected);
//这里引入了inspector,对promise做性能检测
@InspectorInstrumentation.promiseRejected(promise, reason, reactions);
if (!@getByIdDirectPrivate(promise, "promiseIsHandled"))
@hostPromiseRejectionTracker(promise, @promiseRejectionReject);
@triggerPromiseReactions(@promiseStateRejected, reactions, reason);
}
@globalPrivate
function fulfillPromise(promise, value)
{
"use strict";
var reactions = @getByIdDirectPrivate(promise, "promiseReactions");
@putByIdDirectPrivate(promise, "promiseResult", value);
@putByIdDirectPrivate(promise, "promiseReactions", @undefined);
@putByIdDirectPrivate(promise, "promiseState", @promiseStateFulfilled);
//这里引入了inspector,对promise做性能检测
@InspectorInstrumentation.promiseFulfilled(promise, value, reactions);
@triggerPromiseReactions(@promiseStateFulfilled, reactions, value);
}
在PromiseProtoType.js里面我们可以看到 then方法的具体实现
function then(onFulfilled, onRejected)
{
"use strict";
if (!@isPromise(this))
@throwTypeError("|this| is not a object");
var constructor = @speciesConstructor(this, @Promise);
var resultCapability = @newPromiseCapability(constructor);
if (typeof onFulfilled !== "function")
onFulfilled = function (argument) { return argument; };
if (typeof onRejected !== "function")
onRejected = function (argument) { throw argument; };
//把resolve和reject包装一下
var reaction = @newPromiseReaction(resultCapability, onFulfilled, onRejected);
var state = @getByIdDirectPrivate(this, "promiseState");
//如果还是处于pending状态,则把新创建的reaction加入到reactions里面
if (state === @promiseStatePending) {
var reactions = @getByIdDirectPrivate(this, "promiseReactions");
@putByValDirect(reactions, reactions.length, reaction);
} else {//其他状态,rejected或者fullfilled
if (state === @promiseStateRejected && !@getByIdDirectPrivate(this, "promiseIsHandled"))
@hostPromiseRejectionTracker(this, @promiseRejectionHandle);
//这时候Promise本身的任务已经执行完了,调用then则直接把任务加到队列里执行
@enqueueJob(@promiseReactionJob, [state, reaction, @getByIdDirectPrivate(this, "promiseResult")]);
}
@putByIdDirectPrivate(this, "promiseIsHandled", true);
return resultCapability.@promise;
}
Promise.then()调用了enqueueJob ,而enqueueJob实际则先创建了一个microTask,然后调用了queueMicroTask加到任务队列里面。
static EncodedJSValue JSC_HOST_CALL enqueueJob(ExecState* exec)
{
VM& vm = exec->vm();
JSGlobalObject* globalObject = exec->lexicalGlobalObject();
JSValue job = exec->argument(0);
JSValue arguments = exec->argument(1);
ASSERT(arguments.inherits<JSArray>(vm));
globalObject->queueMicrotask(createJSJob(vm, job, jsCast<JSArray*>(arguments)));
return JSValue::encode(jsUndefined());
}
MicroTask
这里解释下MicroTask
MicroTask是一个抽象类,定义了run接口,用于执行一小段任务。
而JSJobMicroTask是他的具体实现,继承自microTask,实现了run接口
class JSJobMicrotask final : public Microtask {
public:
JSJobMicrotask(VM& vm, JSValue job, JSArray* arguments)
{
m_job.set(vm, job);
m_arguments.set(vm, arguments);
}
virtual ~JSJobMicrotask()
{
}
private:
void run(ExecState*) override;
Strong<Unknown> m_job;
Strong<JSArray> m_arguments;
};
Ref<Microtask> createJSJob(VM& vm, JSValue job, JSArray* arguments)
{
return adoptRef(*new JSJobMicrotask(vm, job, arguments));
}
void JSJobMicrotask::run(ExecState* exec)
{
VM& vm = exec->vm();
//获取作用域
auto scope = DECLARE_CATCH_SCOPE(vm);
CallData handlerCallData;
CallType handlerCallType = getCallData(vm, m_job.get(), handlerCallData);
ASSERT(handlerCallType != CallType::None);
//检查参数合法性
MarkedArgumentBuffer handlerArguments;
for (unsigned index = 0, length = m_arguments->length(); index < length; ++index) {
JSValue arg = m_arguments->JSArray::get(exec, index);
CLEAR_AND_RETURN_IF_EXCEPTION(scope, handlerArguments.overflowCheckNotNeeded());
handlerArguments.append(arg);
}
if (UNLIKELY(handlerArguments.hasOverflowed()))
return;
//执行调用
profiledCall(exec, ProfilingReason::Microtask, m_job.get(), handlerCallType, handlerCallData, jsUndefined(), handlerArguments);
scope.clearException();
}
}
run的作用: 获取scope,检查参数合法性,调用profiledCall执行任务
run在drainMicroTask中被调用。
MicroTaskQueue
任务队列,是VM的一个成员变量 ,名称叫m_microTaskQueue, 相关的方法有两个,一个是queueMicroTask,一个是drainMicroTask.
void VM::queueMicrotask(JSGlobalObject& globalObject, Ref<Microtask>&& task)
{
//把任务加入队列末尾
m_microtaskQueue.append(std::make_unique<QueuedTask>(*this, &globalObject, WTFMove(task)));
}
void VM::drainMicrotasks()
{
//把队列中所有任务取出来执行(每次从队列头部取出任务)
while (!m_microtaskQueue.isEmpty())
m_microtaskQueue.takeFirst()->run();
}
drainMicroTask调用的地方有几个:
- jsc.cpp 里面,比如runWithOptions、runInteractive、runJSC,调用jsCore时执行。
- promiseDeferredTimer的doWork函数里面,执行某个延时操作时调用
jsc.cpp
jsc.cpp是javascriptCore工程的main文件,即程序启动的入口文件。调用runJSC就是开始运行javascriptCore。runJSC里面开启了runloop,在mac os和ios上就是CFRunloop,其他平台上也是类似的运行循环。在runJSC里面还启动了一个PromiseDeferredTimer,用来处理延迟操作
template<typename Func>
int runJSC(CommandLine options, bool isWorker, const Func& func)
{
Worker worker(Workers::singleton());
VM& vm = VM::create(LargeHeap).leakRef();
int result;
bool success = true;
GlobalObject* globalObject = nullptr;
{
JSLockHolder locker(vm);
if (options.m_profile && !vm.m_perBytecodeProfiler)
vm.m_perBytecodeProfiler = std::make_unique<Profiler::Database>(vm);
globalObject = GlobalObject::create(vm, GlobalObject::createStructure(vm, jsNull()), options.m_arguments);
globalObject->setRemoteDebuggingEnabled(options.m_enableRemoteDebugging);
func(vm, globalObject, success);
//取出队列中的所有任务并执行
vm.drainMicrotasks();
}
//开启runloop,执行定时任务
vm.promiseDeferredTimer->runRunLoop();
{
JSLockHolder locker(vm);
if (options.m_interactive && success)
runInteractive(globalObject);
}
//......
}
JSRunLoopTimer
JScore利用CFRunloop的timer执行定时操作,跟iOS的NSTimer类似,有scheduleTimer、cancelTimer等方法。内部持有了CFRunLoopTimerRef类型的一个对象m_timer及CFRunloopRef类型的m_runLoop。
promiseDeferredTimer就是继承于JSRunLoopTimer.
class PromiseDeferredTimer : public JSRunLoopTimer {
public:
using Base = JSRunLoopTimer;
PromiseDeferredTimer(VM&);
void doWork() override;
void addPendingPromise(JSPromiseDeferred*, Vector<Strong<JSCell>>&& dependencies);
JS_EXPORT_PRIVATE bool hasPendingPromise(JSPromiseDeferred* ticket);
JS_EXPORT_PRIVATE bool hasDependancyInPendingPromise(JSPromiseDeferred* ticket, JSCell* dependency);
// JSPromiseDeferred should handle canceling when the promise is resolved or rejected.
bool cancelPendingPromise(JSPromiseDeferred*);
typedef std::function<void()> Task;
void scheduleWorkSoon(JSPromiseDeferred*, Task&&);
void stopRunningTasks() { m_runTasks = false; }
JS_EXPORT_PRIVATE void runRunLoop();
private:
HashMap<JSPromiseDeferred*, Vector<Strong<JSCell>>> m_pendingPromises;
Lock m_taskLock;
bool m_runTasks { true };
bool m_shouldStopRunLoopWhenAllPromisesFinish { false };
bool m_currentlyRunningTask { false };
Vector<std::tuple<JSPromiseDeferred*, Task>> m_tasks;
};
} // namespace JSC
promiseDeferredTimer的doWork是在timerDidFire函数中调用的。用来执行具体的任务,可以看到,这里为了避免多线程同时执行任务,还做了加锁操作,这也提现了javascript单线程的特点。
void PromiseDeferredTimer::doWork()
{
ASSERT(m_vm->currentThreadIsHoldingAPILock());
m_taskLock.lock();
cancelTimer();
if (!m_runTasks) {
m_taskLock.unlock();
return;
}
while (!m_tasks.isEmpty()) {
JSPromiseDeferred* ticket;
Task task;
std::tie(ticket, task) = m_tasks.takeLast();
dataLogLnIf(PromiseDeferredTimerInternal::verbose, "Doing work on promise: ", RawPointer(ticket));
// We may have already canceled these promises.
if (m_pendingPromises.contains(ticket)) {
// Allow tasks we run now to schedule work.
m_currentlyRunningTask = true;
m_taskLock.unlock();
task();
m_vm->drainMicrotasks();
m_taskLock.lock();
m_currentlyRunningTask = false;
}
}
if (m_pendingPromises.isEmpty() && m_shouldStopRunLoopWhenAllPromisesFinish) {
#if USE(CF)
CFRunLoopStop(m_runLoop.get());
#else
RunLoop::current().stop();
#endif
}
m_taskLock.unlock();
}
同样继承自JSRunLoopTimer的还有StopIfNecessaryTimer、IncrementalSweeper和GCActivityCallback,这些都是跟JS内存管理相关的类,这里就不展开讲了,后续会专门开一篇讲垃圾回收的实现。
VM中有一个m_runLoopTimers数组维护JSRunLoopTimer。每次调用registerRunLoopTimer就会往m_runLoopTimers中添加一个JSRunLoopTimer。
void VM::registerRunLoopTimer(JSRunLoopTimer* timer)
{
ASSERT(runLoop());
ASSERT(!m_runLoopTimers.contains(timer));
m_runLoopTimers.add(timer);
timer->setRunLoop(runLoop());
}
void VM::unregisterRunLoopTimer(JSRunLoopTimer* timer)
{
ASSERT(m_runLoopTimers.contains(timer));
m_runLoopTimers.remove(timer);
timer->setRunLoop(nullptr);
}
这里runloop()是获取当前线程的runloop
我们知道,iOS的runloop是跟线程一一对应的,那么JS的runloop既然是使用了CFRunloop,那也是跟线程一一对应,那么也就不难理解JS里面的任务为什么是串行的了,因为VM里面的runloop只有一个,只能在一个事件循环中按顺序执行。对于Promise是如此,对于其他的异步任务,如Timer、async/await等也是如此。
网友评论