快速理解Promise

一、事件分发机制


事件分发机制是观察者模式的一种实现,常见于UI交互,给界面组件注册监听器,由界面组件发出事件,监听器接收到事件后作出响应。

事件分发机制也适用于异步管理,其实UI的事件分发也是一种异步通讯,理解Promise实现之前应该对事件分发器(Event Dispatcher)的实现有所了解,DOM对象也可看作是一种分发器,addEventListenerPromise.then类似,都是监听器注册的入口。

二、分发器雏形


实现一个简单的事件分发器Dispatcher,用于封装异步任务task,分发器构造函数须接收一个任务函数task(),对外暴露两个接口,注册监听器register()和执行任务execute(),私有方法trigger()用于触发事件。

接口 参数 说明
register success,error 任务执行成功回调success(),异常回调error()
trigger type,data type事件类型,data附加参数如含异常信息等
execute 执行任务函数task()
function Dispatcher(task)
{
    if(typeof task !=="function")
    {
        throw Error("task is not a function.");
    }

    this.task=task;
    this.listeners=[];
};

Dispatcher.prototype.register=function(on_success=()=>{},on_error=()=>{}){
    this.listeners.push({
        on_success,
        on_error
    });
};

Dispatcher.prototype.trigger=function(type,data)
{
    switch(type)
    {
        case "success":
        {
            this.listeners.forEach((listener)=>
            {
                listener.on_success(data);
            });
        }
            break;
        case "error":
        {
            this.listeners.forEach((listener)=>
            {
                listener.on_error(data);
            });
        }
            break;
    }
};

Dispatcher.prototype.execute=function(){
    const trigger_success=(data)=>
    {
        this.trigger("success",data);
    };

    const trigger_error=(data)=>
    {
        this.trigger("error",data);
    };

    this.task(trigger_success,trigger_error);
};

task()被执行时传入了两个函数trigger_success()trigger_error(),都是对trigger()接口的封装,用于任务结束时反馈执行状态是成功success还是异常error

例2-1 分发器的基础应用:

let task=new Dispatcher((trigger_success,trigger_error)=>
{
    trigger_success();
});

task.register(()=>
{
    console.log("task executed.");
});

task.execute(); // 启动事件源

输出:

task executed.

三、挖坑,实现响应延迟注册


例3-1:

let task=new Dispatcher((trigger_success,trigger_error)=>
{
    trigger_success();
});

task.register(()=>
{
    console.log("task executed.");
});

task.execute(); // 启动事件源

setTimeout(()=>
{
    // 任务结束后注册监听器
    task.register(()=>
    {
        console.log("after 3s."); // 不会被响应
    });
},3000);

输出:

task executed.

如上例子,观察者模式实现的典型是先注册后执行,对于要等到任务结束后才注册监听器的场景遇到的不多,这里挖个坑去实现它。

首先明确,任务的执行结果是不变的(must not transition to any other state),用Promise/A+的话就是说,任务状态分三种,任务状态初始状态是"pending",完成态是"fulfilled",拒绝态是"rejected",状态仅能由初始态转为完成态或拒绝态。

这里定义分发器任务的三个状态为初始态"pending",成功态"success"和异常态"error"
记得注册接口声明为register(on_success,on_error),接口的两个传入函数分别对应成功态和异常态的响应,由分发器负责回调。

任务是否执行成功,由task()实现逻辑确定,执行成功,回调一下trigger_success()进行反馈,分发器自动触发各个监听器。任务结束后注册的监听器,由于脱离了运作流程是不可能得到响应的,“响应延迟注册”换种说法应该是询问,即询问执行结果,只不过询问的接口依然是register(),两种职责集中在一个接口,是不是有违反单一职责原则的嫌疑呢。

事实上,register()接口会发生职责转换,从注册监听器用途转为结果查询用途,任务函数task()尚未执行,此时register()为注册监听器用途,一切按流程走;trigger_success/trigger_error被回调后,预注册的监听器得到反馈,之后register()就转为查询用途。
注册功能原有运作机制已经具有,添加查询用途需要稍作改动,设立标记变量state用于状态的维护和获取,初始化为pending,还有就是缓存任务执行结果(Result)。

function Dispatcher(task)
{
    if(typeof task !=="function")
    {
        throw Error("task is not a function.");
    }

    this.state="pending"; // 维护状态
    this.result=undefined; // 缓存task执行结果

    this.task=task;
    this.listeners=[];
};

任务函数task()回调trigger_success()trigger_error()事实上就是进行状态转换。

Dispatcher.prototype.trigger=function(type,data){
    this.state=type; // 修改状态
    this.result=data; //缓存结果

    switch(type)
    {
        case "success":
        {
            this.listeners.forEach((listener)=>
            {
                listener.on_success(data);
            });
        }
            break;
        case "error":
        {
            this.listeners.forEach((listener)=>
            {
                listener.on_error(data);
            });
        }
            break;
    }
};

当状态不为pending时,可以认为任务已经执行结束,register()接口职责转为结果查询,根据状态反馈结果即可,监听器不需要缓存进listeners[]

Dispatcher.prototype.register=function(on_success=()=>{},on_error=()=>{}){
    switch(this.state)
    {
        case "pending":
        {
            this.listeners.push({
                on_success,
                on_error
            });
        }
            break;
        case "success":
        {
            on_success(this.result);
        }
            break;
        case "error":
        {
            on_error(this.result);
        }
            break;
    }
};

改动后再执行例3-1
输出:

task executed.
after 3s.

四、再挖坑,实现链式调用


链式调用最早出现于jQuery:

$(this).children("a").siblings().removeClass("active").addClass("active");

jQuery链式调用返回this,由于操作对象都是自身,操作结果最终会被复合在一起。但对于事件分发器来说,任务逻辑基本是独立的,链式调用的下一个对象应该是一个新的分发器对象,返回分发器自身会造成自身状态反复改写,违反状态转换原则。
打个草稿,让register()返回一个新的分发器对象,实现任务串联,大概就是如下样子:

let task1=new Dispatcher((trigger_success,trigger_error)=>
{
    trigger_success(); // 确认初始状态,启动链式操作
});

task1.register(()=>
{
    ... // task1 handler
}).register(()=>
{
    ... // task2 handler
}).register(()=>
{
    ... // task3 handler
});

注意,触发链式操作首先要对链头任务进行反馈。

当监听器回调函数on_success()on_error()有返回值,并且返回值形似一个分发器对象(thenable),register()就接受该对象作为返回值。如果监听器没有返回值,那么register()就返回一个默认实现的分发器,并且该分发器的状态继承本分发器的状态(adopt its state)。

register()职责是注册监听器时,由于监听器尚未作出响应自然就无法取得on_success()/on_error()的返回值,此时接口返回的是默认值,后续被注册的观察对象也是该默认值。on_success()/on_error()要被延迟到接口trigger()被启动时回调,由于trigger()接口自身没有定义返回值,回调返回的自定义分发器根本没有机会对外暴露接口,为了将返回的默认值与on_success()/on_error()的自定义分发器内容关联起来,应该将默认的返回值与监听器关联起来一并缓存(deferred),待监听器响应前,将自定义分发器的上下文替换掉被缓存默认值分发器的上下文。

register()职责是查询时,如果监听器返回值具有分发器特征,那么就认为该返回值是一个Dispatcher对象,直接启动即可。

当任务执行异常时,若register()注册的监听器没有实现异常处理on_error(),则将本级的异常状态和数据往下一级分发器传递,直到被接收为止。异常被接收后,为了使执行链能继续执行,若下一级分发器没有自定义实现,则修改下一级分发器状态为"success"继续执行,直到执行链执行完毕。

// 默认的监听器回调实现,用于确保register有返回值,on_success/on_error没有显式被实现可以变相理解为采用了默认实现
// 回调函数返回一个默认实现的分发器,分发器的任务实现就是继承状态和结果(adopt its state),是状态传递的基础
Dispatcher.prototype.on_success=function(data){
    return new Dispatcher(function(trigger_success,trigger_error){
        trigger_success(data); //继承状态 ‘success’和结果result,两者应当同时继承
    });
};

// 见上,默认的on_error实现
Dispatcher.prototype.on_error=function(data){
    return new Dispatcher(function(trigger_success,trigger_error){
        trigger_error(data);
    });
};

// 回调函数给与了默认指定,确保注册的同时返回新的分发器
// 自定义任务内容在需要在回调中实现并返回
Dispatcher.prototype.register=function(on_success=Dispatcher.prototype.on_success,on_error=Dispatcher.prototype.on_error){
    let next; // 下一个分发器

    const noop=(trigger_success,trigger_error)=> // 默认的空任务实现
    {
        switch(this.state) // 本级状态,注意指针,该函数修改下一个分发器的状态
        {
            case "success":
            {
                if(on_success===Dispatcher.prototype.on_success) // 是否实现了回调
                {
                    trigger_success(this.result); // 继承上一级状态数据,下一级state修改为success
                }
                else
                {
                    trigger_success(); // 已实现,回调下一级on_success
                }
            }
                break;
            case "error":
            {
                if(on_error===Dispatcher.prototype.on_error) // 是否实现了回调
                {
                    trigger_error(this.result); // 没有实现就继续路由
                }
                else
                {
                    trigger_success(); // 已实现,可认为异常已处理,回调下一级on_success
                }
            }
        }
    };

    switch(this.state)
    {
        case "pending":
        {
            next=new Dispatcher(noop); // 默认分发器

            this.listeners.push({
                on_success,
                on_error,
                dispatcher:next // 缓存默认值
            });
        }
            break;
        case "success":
        {
            if(typeof on_success==="function")
            {
                next=on_success(this.result); // 产生下一级分发器
                if(!next|| typeof next.register!=="function") // 返回结果是否具有分发器特征
                {
                    next=new Dispatcher(noop);
                }
                next.execute();
            }
        }
            break;
        case "error":
        {
            if(typeof on_error==="function")
            {
                next=on_error(this.result);
                if(!next|| typeof next.register!=="function") // 同上,检查是否有自定义任务的分发器
                {
                    next=new Dispatcher(noop);
                }
                next.execute();
            }
        }
            break;
    }

    return next;
};

Dispatcher.prototype.trigger=function(type,data){
    function switchContext(cache,next)
    {
        // 返回值是否具有分发器特征
        if(typeof next.register==="function")
        {
            cache.task=next.task; // 替换上下文

            next.task=null;
        }
    }

    this.state=type;
    this.result=data;

    switch(type)
    {
        case "success":
        {
            this.listeners.forEach((listener)=>{
                if(typeof listener.on_success==="function")
                {
                    let implement=listener.on_success(data); // 接受自定义实现
                    let deferred=listener.dispatcher;

                    if(implement&& typeof implement.register==="function")
                    {
                        switchContext(deferred,implement); // 替换上下文
                    }
                    deferred.execute(); // 默认执行noop
                }
            });
        }
            break;
        case "error":
        {
            this.listeners.forEach((listener)=>{
                if(typeof listener.on_error==="function")
                {
                    let implement=listener.on_error(data);
                    let deferred=listener.dispatcher;

                    if(implement&& typeof implement.register==="function")
                    {
                        switchContext(deferred,implement);
                    }
                    deferred.execute(); // 默认执行noop
                }
            });
        }
            break;
    }
};

例4-1 状态继承:

let task=new Dispatcher((trigger_success)=>{
    trigger_success(100);
});

task.register().register().register().register((data)=>{
    console.log(data);
});

task.execute();

输出:

100

例4-2 链式操作:

let task1=new Dispatcher((trigger_success,trigger_error)=>
{
    trigger_success();
});

// register 注册状态
task1.register(()=>
{
    console.log("task1 executed.");

    return new Dispatcher((trigger_success,trigger_error)=>
    {
        setTimeout(()=>
        {
            trigger_success();
        },2000)
    });
}).register(()=>
{
    console.log("task2 executed after 2s.");
});

task1.execute(); // 启动任务后,register注册态转查询态

// register 查询状态
task1.register(()=>
{
    return new Dispatcher((trigger_success,trigger_error)=>
    {
        trigger_success();
    });
}).register(()=>
{
    console.log("task3 executed.");
});

输出:

task1 executed.
task3 executed.
task2 executed after 2s.

五、catch()异常捕获


catch()可以看作register()的变形,用于捕获任务异常。

Dispatcher.prototype.catch=function(on_catch){
    return this.register(null,on_catch);
};

稍作修改也可以用于捕获运行时异常。

Dispatcher.prototype.execute=function(){
    const trigger_success=(data)=>{
        this.trigger("success",data);
    };

    const trigger_error=(data)=>{
        this.trigger("error",data);
    };

    try
    {
        this.task(trigger_success,trigger_error);
    }
    catch(e)
    {
        trigger_error(e);
    }
};

五、“去掉”execute()接口


由于已经实现了延迟注册响应,execute()已经没有显式调用的意义了,反正监听器什么时候注册都会得到响应,分发器在构造的时候顺便就把任务启动得了。
现在把execute()接口改为私有(private),只在内部调用,由于私有、公有(public)的概念对JavaScript意义不大,这里就当看不见好了。

function Dispatcher(task)
{
    if(typeof task!=="function")
    {
        throw Error("task is not a function.");
    }

    this.state="pending"; // 维护状态
    this.result=undefined; // 缓存task执行结果

    this.task=task;
    this.listeners=[];

    this.execute(); // *** 启动任务 ***
};

register()接口处于什么职责状态由任务执行时长确定,任务等待外部资源(如AJAX)的时间足够长,所有监听器都会被缓存。

接口register()不需要显式调用execute()了。

// 回调函数给与了默认指定,确保注册的同时返回新的分发器
// 自定义任务内容在需要在回调中实现并返回
Dispatcher.prototype.register=function(on_success=Dispatcher.prototype.on_success,on_error=Dispatcher.prototype.on_error){
    let next; // 下一个分发器

    const noop=(trigger_success,trigger_error)=> // 默认的空任务实现
    {
        switch(this.state) // 本级状态,注意指针,该函数修改下一个分发器的状态
        {
            case "success":
            {
                if(on_success===Dispatcher.prototype.on_success) // 是否实现了回调
                {
                    trigger_success(this.result); // 继承上一级状态数据,下一级state修改为success
                }
                else
                {
                    trigger_success(); // 已实现,回调下一级on_success
                }
            }
                break;
            case "error":
            {
                if(on_error===Dispatcher.prototype.on_error) // 是否实现了回调
                {
                    trigger_error(this.result); // 没有实现就继续路由
                }
                else
                {
                    trigger_success(); // 已实现,可认为异常已处理,回调下一级on_success
                }
            }
        }
    };

    switch(this.state)
    {
        case "pending":
        {
            next=new Dispatcher(noop); // 默认分发器

            this.listeners.push({
                on_success,
                on_error,
                dispatcher:next // 缓存默认值
            });
        }
            break;
        case "success":
        {
            if(typeof on_success==="function")
            {
                next=on_success(this.result); // 产生下一级分发器
                if(!next|| typeof next.register!=="function") // 返回结果是否具有分发器特征
                {
                    next=new Dispatcher(noop);
                }
            }
        }
            break;
        case "error":
        {
            if(typeof on_error==="function")
            {
                next=on_error(this.result);
                if(!next|| typeof next.register!=="function") // 返回结果是否具有分发器特征
                {
                    next=new Dispatcher(noop);
                }
            }
        }
            break;
    }

    return next;
};

由于分发器构造的时候就立即开始任务,被缓存的下一级分发器已经执行完task而上下文都还未切换,被缓存的分发器执行的是“空任务”,不会改变状态,监听器是不会被触发的,因此trigger()接口内还需要保留显式调用execute()

去掉execute()显式调用后,再执行一下例4-2,结果是一样的。

let task1=new Dispatcher((trigger_success,trigger_error)=>
{
    trigger_success();
});

task1.register(()=>
{
    console.log("task1 executed.");

    return new Dispatcher((trigger_success,trigger_error)=>
    {
        setTimeout(()=>
        {
            trigger_success();
        },2000)
    });
}).register(()=>
{
    console.log("task2 executed after 2s.");
});

task1.register(()=>
{
    return new Dispatcher((trigger_success,trigger_error)=>
    {
        trigger_success();
    });
}).register(()=>
{
    console.log("task3 executed.");
});

输出:

task1 executed.
task3 executed.
task2 executed after 2s.

Dispatcher.js

function Dispatcher(task)
{
    if(typeof task!=="function")
    {
        throw Error("task is not a function.");
    }

    this.state="pending"; // 维护状态
    this.result=undefined; // 缓存task执行结果

    this.task=task;
    this.listeners=[];

    this.execute();
};

// 默认的监听器回调实现,用于确保register有返回值,on_success/on_error没有显式被实现可以变相理解为采用了默认实现
// 回调函数返回一个默认实现的分发器,分发器的任务实现就是继承状态和结果(adopt its state),是状态传递的基础
Dispatcher.prototype.on_success=function(data){
    return new Dispatcher(function(trigger_success,trigger_error){
        trigger_success(data); //继承状态 ‘success’和结果,两者应当同时继承
    });
};

// 见上,默认的on_error实现
Dispatcher.prototype.on_error=function(data){
    return new Dispatcher(function(trigger_success,trigger_error){
        trigger_error(data);
    });
};

Dispatcher.prototype.catch=function(on_catch){
    return this.register(null,on_catch);
};

// 回调函数给与了默认指定,确保注册的同时返回新的分发器
// 自定义任务内容在需要在回调中实现并返回
Dispatcher.prototype.register=function(on_success=Dispatcher.prototype.on_success,on_error=Dispatcher.prototype.on_error){
    let next; // 下一个分发器

    const noop=(trigger_success,trigger_error)=> // 默认的空任务实现
    {
        switch(this.state) // 本级状态,注意指针,该函数修改下一个分发器的状态
        {
            case "success":
            {
                if(on_success===Dispatcher.prototype.on_success) // 是否实现了回调
                {
                    trigger_success(this.result); // 继承上一级状态数据,下一级state修改为success
                }
                else
                {
                    trigger_success(); // 已实现,回调下一级on_success
                }
            }
                break;
            case "error":
            {
                if(on_error===Dispatcher.prototype.on_error) // 是否实现了回调
                {
                    trigger_error(this.result); // 没有实现就继续路由
                }
                else
                {
                    trigger_success(); // 已实现,可认为异常已处理,回调下一级on_success
                }
            }
        }
    };

    switch(this.state)
    {
        case "pending":
        {
            next=new Dispatcher(noop); // 默认分发器

            this.listeners.push({
                on_success,
                on_error,
                dispatcher:next // 缓存默认值
            });
        }
            break;
        case "success":
        {
            if(typeof on_success==="function")
            {
                next=on_success(this.result); // 产生下一级分发器
                if(!next|| typeof next.register!=="function") // 返回结果是否具有分发器特征
                {
                    next=new Dispatcher(noop);
                }
            }
        }
            break;
        case "error":
        {
            if(typeof on_error==="function")
            {
                next=on_error(this.result);
                if(!next|| typeof next.register!=="function") // 返回结果是否具有分发器特征
                {
                    next=new Dispatcher(noop);
                }
            }
        }
            break;
    }

    return next;
};

Dispatcher.prototype.trigger=function(type,data){
    function switchContext(cache,next)
    {
        // 返回值是否具有分发器特征
        if(typeof next.register==="function")
        {
            cache.task=next.task; // 替换上下文

            next.task=null;
        }
    }

    this.state=type;
    this.result=data;

    switch(type)
    {
        case "success":
        {
            this.listeners.forEach((listener)=>{
                if(typeof listener.on_success==="function")
                {
                    let implement=listener.on_success(data); // 接受自定义实现
                    let deferred=listener.dispatcher;

                    if(implement&& typeof implement.register==="function")
                    {
                        switchContext(deferred,implement); // 替换上下文
                    }
                    deferred.execute(); // 执行自定义的task
                }
            });
        }
            break;
        case "error":
        {
            this.listeners.forEach((listener)=>{
                if(typeof listener.on_error==="function")
                {
                    let implement=listener.on_error(data);
                    let deferred=listener.dispatcher;

                    if(implement&& typeof implement.register==="function")
                    {
                        switchContext(deferred,implement);
                    }
                    deferred.execute();
                }
            });
        }
            break;
    }
};

Dispatcher.prototype.execute=function(){
    const trigger_success=(data)=>{
        this.trigger("success",data);
    };

    const trigger_error=(data)=>{
        this.trigger("error",data);
    };

    try
    {
        this.task(trigger_success,trigger_error);
    }
    catch(e)
    {
        trigger_error(e);
    }
};

六、Promise


Promise原理与事件分发同源,但语义与事件分发、观察者模式等差太远,不容易直接理解。
Dispatcher.register类似Promise.thenPromise.then延迟注册响应的特点会让人产生Promise对象可以并行执行的错觉。

打开文本编辑器,对Dispatcher代码进行一些文本替换,就是一个简陋的Promise

Promise.js

function Promise(task)
{
    if(typeof task!=="function")
    {
        throw Error("task is not a function.");
    }

    this.state="pending";
    this.result=undefined;

    this.task=task;
    this.listeners=[];

    this.execute();
};

Promise.prototype.on_resolve=function(data){
    return new Promise(function(resolve,reject){
        resolve(data);
    });
};

Promise.prototype.on_reject=function(data){
    return new Promise(function(resolve,reject){
        reject(data);
    });
};

Promise.prototype.catch=function(on_catch){
    return this.then(null,on_catch);
};

Promise.prototype.then=function(on_resolve=Promise.prototype.on_resolve,on_reject=Promise.prototype.on_reject){
    let next;

    const noop=(resolve,reject)=>
    {
        switch(this.state)
        {
            case "resolved":
            {
                if(on_resolve===Promise.prototype.on_resolve)
                {
                    resolve(this.result);
                }
                else
                {
                    resolve();
                }
            }
                break;
            case "rejected":
            {
                if(on_reject===Promise.prototype.on_reject)
                {
                    reject(this.result);
                }
                else
                {
                    resolve();
                }
            }
        }
    };

    switch(this.state)
    {
        case "pending":
        {
            next=new Promise(noop);

            this.listeners.push({
                on_resolve,
                on_reject,
                promise:next
            });
        }
            break;
        case "resolved":
        {
            if(typeof on_resolve==="function")
            {
                next=on_resolve(this.result);
                if(!next|| typeof next.then!=="function")
                {
                    next=new Promise(noop);
                }
            }
        }
            break;
        case "rejected":
        {
            if(typeof on_reject==="function")
            {
                next=on_reject(this.result);
                if(!next|| typeof next.then!=="function")
                {
                    next=new Promise(noop);
                }
            }
        }
            break;
    }

    return next;
};

Promise.prototype.trigger=function(type,data){
    function switchContext(cache,next)
    {
        if(typeof next.then==="function")
        {
            cache.task=next.task;

            next.task=null;
        }
    }

    this.state=type;
    this.result=data;

    switch(type)
    {
        case "resolved":
        {
            this.listeners.forEach((listener)=>{
                if(typeof listener.on_resolve==="function")
                {
                    let implement=listener.on_resolve(data);
                    let deferred=listener.promise;

                    if(implement&& typeof implement.then==="function")
                    {
                        switchContext(deferred,implement);
                    }
                    deferred.execute();
                }
            });
        }
            break;
        case "rejected":
        {
            this.listeners.forEach((listener)=>{
                if(typeof listener.on_reject==="function")
                {
                    let implement=listener.on_reject(data);
                    let deferred=listener.promise;

                    if(implement&& typeof implement.then==="function")
                    {
                        switchContext(deferred,implement);
                    }
                    deferred.execute();
                }
            });
        }
            break;
    }
};

Promise.prototype.execute=function(){
    const resolve=(data)=>{
        this.trigger("resolved",data);
    };

    const reject=(data)=>{
        this.trigger("rejected",data);
    };

    try
    {
        this.task(resolve,reject);
    }
    catch(e)
    {
        reject(e);
    }
};

例6-1:

new Promise(function(resolve,reject){
    console.log('p1 resolved.');
    resolve(100);
}).then(function(){
    return new Promise(function(resolve,reject){
        setTimeout(function(){
            console.log('p2 rejected.');
            reject(200);
        },2000);
    });
}).then(function(){
    return new Promise(function(resolve,reject){
        console.log('p3 resolved.');
        resolve(300);
    });
}).then().then().then(function(data){
    console.log('data: '+data+" after 2s.");
}).catch().catch().catch(function(error){
    console.log('error: '+error+" after 2s.");
}).then(function(){
    console.log(500);
},function(){
    console.log(400);
});

输出:

p1 resolved.
p2 rejected.
error: 200 after 2s.
500

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,585评论 18 139
  • 官方中文版原文链接 感谢社区中各位的大力支持,译者再次奉上一点点福利:阿里云产品券,享受所有官网优惠,并抽取幸运大...
    HetfieldJoe阅读 8,676评论 0 29
  • title: promise总结 总结在前 前言 下文类似 Promise#then、Promise#resolv...
    JyLie阅读 12,215评论 1 21
  • 你不知道JS:异步 第三章:Promises 接上篇3-1 错误处理(Error Handling) 在异步编程中...
    purple_force阅读 1,384评论 0 2
  • 这两天破了酒戒了,前天晚上是啤酒,四个人一箱,对他们而言量不大,于我而言就有些多了,睡到半夜起来吐了一会,不胜酒力...
    生杏子阅读 216评论 2 1