2017-03-05 72 views
1

我想了解differences between es6 promises and regular callbacks,但沒有得到下面的例子。有人可以通過回調顯示下面的樣子嗎?承諾是不可變的,它們的保證價值是什麼意思?

// an immediately resolved promise 
var p2 = Promise.resolve("foo"); 

// can get it after the fact, unlike events 
p2.then((res) => console.log(res)); 

var p = new Promise(function(resolve, reject) { 
    setTimeout(() => resolve(4), 2000); 
}); 

// handler can't change promise, just value 
p.then((res) => { 
    res += 2; 
    console.log(res); 
}); 

// still gets 4 
p.then((res) => console.log(res)); 
+1

一個承諾是一個單向鎖。一個是用一個價值來解決或者被一個理由拒絕的,它的狀態和價值/理性永遠不會改變。所以,無論你做了多少次「.then()」,都會得到相同的結果。普通的回調只是回調函數,每次調用時都可以賦予不同的值。如果您希望在某個操作完成時只收到一次通知,請使用承諾。如果您想多次收到通知,請使用普通回調或偵聽器或觀察者或其他可以觸發多次的其他機制。 – jfriend00

回答

2

爲了與標準回調系統進行比較,我們來創建一個可以產生這種通知對象的類。這將有一個接口很像Promise有,但它實現了一個簡單的回調系統:

class Notifier { 
    constructor(executor) { 
     // The object will maintain a list of callbacks 
     this.callbacks = []; 
     // The executor is executed now and will allow the client 
     // to determine the logic of this notifier object: 
     // ...when the callbacks need to be called and with which value: 
     executor((res) => { 
      // The client can call this resolve function to indicate 
      // the value. So now the callbacks need to be called with it: 
      this.callbacks.forEach(callback => callback(res)); 
     }); 
    } 
    addListener(callback) { 
     // This method resembles the `then` of promises: it allows 
     // a client to pass a callback function, that should be called 
     // when the value becomes available (i.e. when the event triggers). 
     this.callbacks.push(callback); 
    } 
}; 

所以,像Promise,你可以通過這個類的構造函數做一些工作,並指示值在適當的時候。您也可以將偵聽器附加到該偵聽器,該值將在該值可用時被調用。

這最後一句突出顯示了一個重要的事實:如果值變得可用,但是您還沒有附加偵聽器(回調函數),那麼即使將偵聽器附加在事實之後,您也會錯過通知。

這裏是基於回調的代碼,你可以和你一起從文章引用的代碼比較:

class Notifier { 
 
    constructor(executor) { 
 
     // The object will maintain a list of callbacks 
 
     this.callbacks = []; 
 
     // The executor is executed now and will allow the client 
 
     // to determine the logic of this notifier object: 
 
     // ...when the callbacks need to be called and with which value: 
 
     executor((res) => { 
 
      // The client can call this resolve function to indicate 
 
      // the value. So now the callbacks need to be called with it: 
 
      this.callbacks.forEach(callback => callback(res)); 
 
     }); 
 
    } 
 
    addListener(callback) { 
 
     // This method resembles the `then` of promises: it allows 
 
     // a client to pass a callback function, that should be called 
 
     // when the value becomes available (i.e. when the event triggers). 
 
     this.callbacks.push(callback); 
 
    } 
 
}; 
 

 
// a notifier that immediately notifies the result 
 
f2 = new Notifier((resolve) => resolve("foo")); 
 
// but since no-one was listening, no callback is called. 
 

 
// canNOT get it after the fact, unlike promises 
 
f2.addListener((res) => console.log(res)); 
 
// ... nothing gets called or printed: we are too late. 
 

 
// 
 
var f = new Notifier(function(resolve) { 
 
    setTimeout(() => resolve({ data: 4}), 2000); 
 
}); 
 

 
// handler CAN change the outcome 
 
f.addListener((res) => { 
 
    res.data += 2; 
 
    console.log(res.data); 
 
}); 
 

 
// ... now also this one gets 6! 
 
f.addListener((res) => console.log(res.data));

+0

你想要什麼時候使用通知器來立即通知結果?我認爲承諾的重點是異步? – stackjlei

+0

承諾對調用'then'回調確實是異步的。通知人通常可以在不同時間點召喚回調,每次都有不同的目的和價值。如果通知程序在創建時立即調用回調函數,它確實不是非常有用。但是這是爲了比較最簡單的回調實現(它不必保證異步調用)與您引用的文章中介紹的承諾實現。 – trincot

+1

當然,如果你實現了承諾從這樣一個通告者對象開始的所有特性(和限制),那麼你可以使它像一個承諾一樣行事。參見[我的其他答案](http://stackoverflow.com/a/42057900/5459839),其中我從頭開始提供了實現承諾概念的代碼。 – trincot

-1

當promise變量被解析時,它被調用時解析的值返回。 要使用多個,您必須按如下方式調用它。

var p = new Promise(function(resolve, reject) { 
    setTimeout(() => resolve(4), 2000); 
}); 

p.then((res) => { 
    res += 2; 
    console.log(res); 
    return res 
}) 
.then((res) => console.log(res)); 
+0

這並不能解釋回調和承諾之間的區別...... – Li357

2

承諾是一個單向鎖存器。一旦以價值解決或以理由拒絕,其狀態和價值/理由永遠不會改變。所以,不管你做了多少次.then()這個承諾,你總是會得到相同的結果。這就是「不變」的意思。

我不確定你的意思是保證值。不能保證承諾永遠解決。它可能會拒絕(因此沒有價值),或者如果操作沒有完成,它可能永遠不會解決或拒絕。

操作承諾類型的一個示例是爲異步操作(如Ajax調用或從文件中讀取某些字節)而設計的。操作是異步的(操作開始後解釋器的正常執行繼續),操作具有特定的開始和結束。在大多數情況下,操作可能會成功完成,在這種情況下,操作可能會有值,或者可能會以錯誤結束,在這種情況下會有錯誤。值和錯誤都可以是對象,所以如果結果不僅僅是一個簡單的值,它們可以有很多屬性。

一個Ajax調用,例如有一個特定的開始和結束。它不能一次結束,因此它是承諾的完美搭配。你得到了一個表示ajax操作最終結果的承諾。然後註冊滿足處理程序和拒絕處理程序,並在操作完成時調用其中一個或另一個。

普通回調函數只是回調函數,每次調用它們時可以賦予不同的值,並且可以多次調用它們。

如果您希望在某個操作完成且操作具有特定開始和結束時只收到一次通知,請使用承諾。

如果您想多次收到通知,請使用普通回調或事件偵聽器或觀察者或其他可以多次觸發的機制。


作爲一個簡單的例子,setTimeout()非常適合承諾。

function delay(t) { 
    return new Promise((resolve, reject) => { 
     resolve(); 
    }, t); 
} 

// delay 100ms before starting the operation 
delay(100).then(run); 

或者使用藍鳥無極庫循環的URL列表一點更多地參與操作,下載內容,分析的內容,看在內容對於一些特定的URL,然後將它們收集齊全(否則被稱爲刮):

const Promise = require('bluebird'); 
const request = Promise.promisifyAll(require('request'), {multiArgs: true}); 
const cheerio = require('cheerio'); 

function getAsync() { 
    return request.getAsync.apply(request, arguments).then(argArray => { 
     // return only the response html 
     if (argArray[0].statusCode !== 200) { 
      throw new Error("response statusCode = " + argArray[0].statusCode); 
     } 
     return argArray[1]; 
    }); 
} 

const urls = [....]; 
Promise.mapSeries(urls, url => { 
    return getAsync({url: url, gzip: true}).then(html => { 
     let $ = cheerio.load(html); 
     let resources = $("#external_resources_list li a.filename"); 
     resources.each(index, link) => { 
      let href = $(link).attr("href"); 
      console.log(href); 
      results.push(href); 
     }); 
    }).catch(err => { 
     // log error, but keep going 
     console.log(url, err); 
    }); 
}).then(() => { 
    // got all results here 
    console.log(results); 
}); 

而且,setInterval()根本不會有承諾,因爲它要反覆每次通知的時間間隔通行證,這將根本無法與承諾攜手。堅持回撥setInterval()

+0

爲什麼一個承諾只能讓你得到通知一次?每次發出AJAX請求時,都不會創建新的承諾嗎?因此,每個承諾都對應於每個AJAX請求,那麼這就是你如何得到多次通知的權利? – stackjlei

+0

保證,我指的是鏈接文章的作者如何說:「無論我們何時註冊處理程序,即使它已經解決,我們都會保證收到價值(與事件相反,這可能會導致競爭條件)。」 – stackjlei

+0

@stackjlei - 是的,如果你創建一個新的承諾並註冊一個新的'.then()'處理程序,那麼在新的承諾中將會有一個新狀態變化的通知。我的觀點是,一個給定的承諾只有一個國家的變化。之後,它不能更改狀態,因此不能重新用於新的異步操作。 – jfriend00

相關問題