Javascript 使用promises的语法编写同步代码有什么好处吗

Javascript 使用promises的语法编写同步代码有什么好处吗,javascript,promise,Javascript,Promise,有同步承诺这样的概念吗?使用承诺的语法编写同步代码会有什么好处吗 try { foo(); bar(a, b); bam(); } catch(e) { handleError(e); } …可以这样编写(但是使用同步版本的然后) 有同步承诺这样的概念吗 本杰明是绝对正确的。然而,它们不是唯一的类型 如果你还没有意识到,那么你可能想知道什么是单子。网上有很多关于单子的解释。然而,他们中的大多数人都患有癌症 简言之,谬误在于大多数理解单子的人并不真正知道如何向他人解释这个概念。简

有同步承诺这样的概念吗?使用承诺的语法编写同步代码会有什么好处吗

try {
  foo();
  bar(a, b);
  bam();
} catch(e) {
  handleError(e);
}
…可以这样编写(但是使用同步版本的
然后

有同步承诺这样的概念吗

本杰明是绝对正确的。然而,它们不是唯一的类型

如果你还没有意识到,那么你可能想知道什么是单子。网上有很多关于单子的解释。然而,他们中的大多数人都患有癌症

简言之,谬误在于大多数理解单子的人并不真正知道如何向他人解释这个概念。简单地说,单子是一个抽象概念,人类很难掌握抽象概念。然而,人类很容易摸索出具体的概念

让我们从一个具体的概念开始理解单子。正如我所说,单子是一个抽象的概念。这意味着monad是没有an的(即,它定义某些操作并指定这些操作应该做什么,而不指定必须如何做)

现在,有不同类型的单子。每种类型的单子都是具体的(即它定义了单子的一种类型)。承诺是单子的一种。因此,承诺是单子的一个具体例子。因此,如果我们研究承诺,那么我们就可以开始理解单子

那么我们从哪里开始呢?幸运的是,用户在回答您的问题时给了我们一个很好的起点:


我能想到的一个例子是将承诺与同步代码链接在一起。在找到这个问题的答案时:我将一个同步调用包装在一个承诺中,以便能够将它们与其他承诺联系起来

让我们看看他的代码:

var run = function() {
    getScenario()
    .then(mapToInstruction)
    .then(waitForTimeout)
    .then(callApi)
    .then(handleResults)
    .then(run);
};
这里,
run
函数返回一个承诺,该承诺由
getScenario
mapToInstruction
waitForTimeout
callApi
handleResults
run
本身链接在一起返回的承诺组成

现在,在我们继续之前,我想向您介绍一种新的符号,以可视化这些函数的作用:

run::Unit->Deferred a
getScenario::Unit->延迟数据
mapToInstruction::Data->延迟指令
waitForTimeout::指令->延迟指令
callApi::指令->延迟数据
HandlerResults::数据->延迟单位
下面是分类:

  • ::
    符号表示“属于该类型”,而
    ->
    符号表示“到”。因此,例如,
    run::Unit->Deferred a
    读作“
    run
    属于
    Unit
    to
    Deferred a
    ”类型
  • 这意味着
    run
    是一个函数,它接受
    单位
    值(即无参数),并返回类型为
    延迟a
    的值
  • 这里,
    a
    表示任何类型。我们不知道
    a
    是什么类型,也不关心
    a
    是什么类型。因此,它可以是任何类型
  • 这里,
    Deferred
    是一种承诺数据类型(具有不同的名称),而
    Deferred a
    意味着当承诺被解析时,它会产生类型为
    a
    的值
  • 从上述可视化中,我们可以学到以下几点:

  • 每个函数都接受一些值并返回一个承诺
  • 每个承诺返回的解析值成为下一个函数的输入:

    // Identity :: a -> Identity a
    
    function Identity(value) {
        this.value = value;
    }
    
    // then :: Identity a -> (a -> Identity b) -> Identity b
    
    Identity.prototype.then = function (f) {
        return f(this.value);
    };
    
    // one :: Identity Number
    
    var one = new Identity(1);
    
    // yes :: Identity Boolean
    
    var yes = bind(one, isOdd);
    
    // isOdd :: Number -> Identity Boolean
    
    function isOdd(n) {
        return new Identity(n % 2 === 1);
    }
    
    // compose :: (b -> c) -> (a -> b) -> a -> c
    //            |______|    |______|
    //                |           |
    function compose( f,          g) {
    
        // compose(f, g) :: a -> c
        //                  |
        return function (   x) {
            return f(g(x));
        };
    }
    
    var isOdd = compose(unit, odd);
    
    run::Unit->Deferred a
    getScenario::Unit->延迟数据
    getScenario::Unit->延迟数据
    mapToInstruction::Data->延迟指令
    mapToInstruction::Data->延迟指令
    waitForTimeout::指令->延迟指令
    waitForTimeout::指令->延迟指令
    callApi::指令->延迟数据
    callApi::指令->延迟数据
    HandlerResults::数据->延迟单位
    HandlerResults::数据->延迟单位
    运行::单元->延迟a
    
  • 在解析前一个承诺之前,下一个函数无法执行,因为它必须使用前一个承诺的解析值

  • 正如我前面提到的,monad是一个定义特定操作的函数。monad接口提供的操作之一是链接monad的操作。对于承诺,这是
    然后
    方法。例如:

    getScenario().then(mapToInstruction)
    
    我们知道:

    getScenario::Unit->递延数据
    mapToInstruction::Data->延迟指令
    
    因此:

    getScenario()::延迟数据——因为调用getScenario时
    --返回延迟的数据值
    
    我们还知道:

    getScenario()。然后(映射到指令)::延迟指令
    
    因此,我们可以推断:

    then::递延a->(a->递延b)->递延b
    
    换句话说,“
    然后
    是一个函数,它接受两个参数(类型为
    延迟a
    的值和类型为
    a->延迟b
    )的函数),并返回类型为
    延迟b
    ”的值,因此:

    then::递延a->(a->递延b)->递延b
    getScenario()::延迟数据
    --因此,由于a=数据
    getScenario()。然后::(数据->延迟b)->延迟b
    mapToInstruction::Data->延迟指令
    --因此,由于b=指令
    getScenario()。然后(mapInstruction)::延迟指令
    
    所以我们进行了第一次monad手术:

    then::递延a->(a->递延b)->递延b
    
    然而,这一行动是具体的。这是spe
    // Identity :: a -> Identity a
    
    function Identity(value) {
        this.value = value;
    }
    
    // then :: Identity a -> (a -> Identity b) -> Identity b
    
    Identity.prototype.then = function (f) {
        return f(this.value);
    };
    
    // one :: Identity Number
    
    var one = new Identity(1);
    
    // yes :: Identity Boolean
    
    var yes = bind(one, isOdd);
    
    // isOdd :: Number -> Identity Boolean
    
    function isOdd(n) {
        return new Identity(n % 2 === 1);
    }
    
    // mapToInstruction :: Data -> Deferred Instruction
    
    // The result of the previous promise is passed into the 
    // next as we're chaining. So the data will contain the 
    // result of getScenario
    var mapToInstruction = function (data) {
        // We map it onto a new instruction object
        var instruction = {
            method: data.endpoints[0].method,
            type: data.endpoints[0].type,
            endpoint: data.endpoints[0].endPoint,
            frequency: data.base.frequency
        };
    
        console.log('Instructions recieved:');
        console.log(instruction);
    
        // And now we create a promise from this
        // instruction so we can chain it
        var deferred = $.Deferred();
        deferred.resolve(instruction);
        return deferred.promise();
    };
    
    // And now we create a promise from this
    // instruction so we can chain it
    var deferred = $.Deferred();
    deferred.resolve(instruction);
    return deferred.promise();
    
    // handleResults :: Data -> Deferred Unit
    
    var handleResults = function(data) {
        console.log("Handling data ...");
        var deferred = $.Deferred();
        deferred.resolve();
        return deferred.promise();
    };
    
    // unit :: a -> Deferred a
    
    function unit(value) {
        var deferred = $.Deferred();
        deferred.resolve(value);
        return deferred.promise();
    }
    
    // mapToInstruction :: Data -> Deferred Instruction
    
    // The result of the previous promise is passed into the 
    // next as we're chaining. So the data will contain the 
    // result of getScenario
    var mapToInstruction = function (data) {
        // We map it onto a new instruction object
        var instruction = {
            method: data.endpoints[0].method,
            type: data.endpoints[0].type,
            endpoint: data.endpoints[0].endPoint,
            frequency: data.base.frequency
        };
    
        console.log('Instructions recieved:');
        console.log(instruction);
    
        return unit(instruction);
    };
    
    // handleResults :: Data -> Deferred Unit
    
    var handleResults = function(data) {
        console.log("Handling data ...");
        return unit();
    };
    
    // isOdd :: Number -> Identity Boolean
    
    function isOdd(n) {
        return new Identity(n % 2 === 1);
    }
    
    // odd :: Number -> Boolean
    
    function odd(n) {
        return n % 2 === 1;
    }
    
    // unit :: a -> Identity a
    
    function unit(value) {
        return new Identity(value);
    }
    
    // isOdd :: Number -> Identity Boolean
    
    function idOdd(n) {
        return unit(odd(n));
    }
    
    // compose :: (b -> c) -> (a -> b) -> a -> c
    //            |______|    |______|
    //                |           |
    function compose( f,          g) {
    
        // compose(f, g) :: a -> c
        //                  |
        return function (   x) {
            return f(g(x));
        };
    }
    
    var isOdd = compose(unit, odd);
    
    // Given:
    
    // x :: a
    // f :: Monad m => a -> m b
    // h :: Monad m => m a
    // g :: Monad m => b -> m c
    
    // we have the following three laws:
    
    // 1. Left identity
    
    bind(unit(x), f)    === f(x)
    
    unit(x).then(f)     === f(x)
    
    // 2. Right identity
    
    bind(h, unit)       === h
    
    h.then(unit)        === h
    
    // 3. Associativity
    
    bind(bind(h, f), g) === bind(h, function (x) { return bind(f(x), g); })
    
    h.then(f).then(g)   === h.then(function (x) { return f(x).then(g); })
    
    // unit :: a -> Array a
    
    function unit(x) {
        return [x, x];
    }
    
    // concat :: Array (Array a) -> Array a
    
    function concat(h) {
        return h.concat.apply([], h);
    }
    
    // bind :: Array a -> (a -> Array b) -> Array b
    
    function bind(h, f) {
        return concat(h.map(f));
    }
    
    // 2. Right identity
    
    bind(h, unit) === h
    
    // proof
    
    var h   = [1,2,3];
    
    var lhs = bind(h, unit) = [1,1,2,2,3,3];
    
    var rhs = h = [1,2,3];
    
    lhs !== rhs;
    
    // unit :: a -> Array a
    
    function unit(x) {
        return [x];
    }
    
    Identity.of(1).chain(isOdd);
    
    try {
      foo();
      bar(a, b);
      bam();
    } catch(e) {
      handleError(e);
    }
    
    foo()
      .then(bar.bind(a, b))
      .then(bam)
      .fail(handleError)
    
    function CanFail() {}
    
    // Fail :: f -> CanFail f a
    
    function Fail(error) {
        this.error = error
    }
    
    Fail.prototype = new CanFail;
    
    // Okay :: a -> CanFail f a
    
    function Okay(value) {
        this.value = value;
    }
    
    Okay.prototype = new CanFail;
    
    // then :: CanFail f a -> (a -> CanFail f b) -> CanFail f b
    
    CanFail.prototype.then = function (f) {
        return this instanceof Okay ? f(this.value) : this;
    };
    
    // foo :: Unit -> CanFail Number Boolean
    
    function foo() {
        if (someError) return new Fail(1);
        else return new Okay(true);
    }
    
    // bar :: String -> String -> Boolean -> CanFail Number String
    
    function bar(a, b) {
        return function (c) {
            if (typeof c !== "boolean") return new Fail(2);
            else return new Okay(c ? a : b);
        };
    }
    
    // bam :: String -> CanFail Number String
    
    function bam(s) {
        if (typeof s !== "string") return new Fail(3);
        else return new Okay(s + "!");
    }
    
    // handleError :: Number -> Unit
    
    function handleError(n) {
        switch (n) {
        case 1: alert("unknown error");    break;
        case 2: alert("expected boolean"); break;
        case 3: alert("expected string");  break;
        }
    }
    
    // result :: CanFail Number String
    
    var result = foo()
                .then(bar("Hello", "World"))
                .then(bam);
    
    if (result instanceof Okay)
        alert(result.value);
    else handleError(result.error);
    
    var a = 'initial value',
        b = foo(a, 'more', 'arguments'),
        // ...
        result = bar(z);
    
    on('initial value')
        .do(_.partialRight(foo, 'more', 'arguments'))
        // ...
        .do(bar)
        .do(function (result) {
            // ...
        });