2016-01-07 75 views
9

如何推遲承諾鏈?我需要這個,因爲我想在繼續使用腳本之前等待CSS動畫完成。如何在功能上延遲承諾

該功能的目的是打開一個視圖。如果視圖尚未打開,請打開它(通過更改類),等待css動畫,繼續。如果視圖已經打開,則不要執行任何操作並繼續。

我要調用的函數是這樣的: (這是一個角度控制器內的功能)

$scope.openView(viewId).then(function() {    
    $scope.openAnotherView(anotherViewId); 
}); 


/** Function to open some view **/ 
$scope.openView = function (viewId) { 
    function timeout(delay) { 
     return new Promise(function(resolve, reject) { 
      $timeout(resolve, delay); 
     }); 
    } 

    // Check if view is already open 
    if ($scope.viewId != viewId) { 
     $scope.viewId = viewId;    

     // get data from ajaxcall (also a promise) 
     return MyService.getData(viewId).then(function(data) { 
      // add data to view 
      // change class to open view 
      // this is working ok! 
     }).then(function() { 
      return timeout(30000 /* some large number (testing purpose) */) 
     }); 
    } else { 
     // view is already open, so return here we don't have to wait 
     // return empty promise, with no timeout 
     return new Promise(function(resolve, reject) { 
      resolve() 
     });  
    } 
} 

此代碼的工作,卻遲遲不工作。我的方法好嗎?我在這裏錯過了什麼?


編輯1:改善與建議的代碼從@sdgluck


編輯2:一些澄清的主要問題的:

爲了澄清的主要問題一bit more: 我可以在我的代碼中使用這種構造嗎?

// code doesnt know wheter to wait or not 
// can the Promise do this? 
openView().then(function() {    
    openAnotherView(); 
} 

成果1

瀏覽器會調用openView(),但因爲它已經打開,將只需要調用openAnotherView()馬上(無延遲)。

成果2

的觀點是不公開的,所以openView()會打開它,然後延遲(?或@Dominic託拜厄斯指出,添加一個eventlister)然後調用openAnotherView()一些延遲之後。

感謝您的幫助!

編輯3:添加了該問題的解釋小提琴 http://jsfiddle.net/C3TVg/60/

+1

我想你可能會以這種錯誤的方式去做。這聽起來像你可能想要檢測動畫是否完成像[this](https://davidwalsh.name/css-animation-callback)。 –

回答

1

如何推遲承諾鏈?

$timeout返回承諾。 返回諾言它。

$scope.openView = function (viewId) { 
    // Check if view is already open 
    if ($scope.viewId == viewId) { 
     //chain right away with empty promise 
     return $q.when(); 
    }; 

    //otherwise if view is not already open 

    var p = MyService.getData(viewId).then(function(data) { 
      // add data to view 
      // change class to open view 
      // this is working ok! 
    }); 

    var pDelayed = p.then (function() { 
     //return to chain delay 
     return $timeout(angular.noop, 30000); 
     }); 

    //return delayed promise for chaining 
    return pDelayed; 
}; 

$scope.openView(viewId).then(function() { 
    //use chained promise    
    $scope.openAnotherView(anotherViewId); 
}); 
+0

Thx爲您的答案,我剛剛更新了我的問題。我試圖澄清一個事實,即如果我必須等待,我現在不會事先做好。 – 11mb

+0

更新爲添加條件延遲。 – georgeawg

3

每個then accepts a function應返回的承諾。它不接受Promise實例。你要調用返回timeout

return MyService 
    .getData(viewId) 
    .then(function(data) { 
     // ... 
    }) 
    .then(function() { 
     return timeout(3000); 
    }); 

另外,有timeout回報的功能,而不是一個承諾:

function timeout(delay) { 
    return function() { 
     return new Promise(function(resolve, reject) { 
      //      ^^^^^^^ (misspelt in your example) 
      $timeout(resolve, delay); 
     }); 
    }; 
} 

然後你就可以把它作爲你的例子:

return MyService 
    .getData(viewId) 
    .then(function(data) { 
     // ... 
    }) 
    .then(timeout(3000)); 
+0

Thx用於通過timout函數識別我的錯誤並清除如何解決它。但是我認爲@Dominic Tobias指出我的代碼存在另一個問題。 – 11mb

+1

@ 11mb沒問題。是的,其他答案更全面。 :-) – sdgluck

13

要延期承諾,只需在等待時間後致電resolve函數即可。

new Promise(function(resolve, reject) { 
    setTimeout(function() { 
    resolve(); 
    }, 3000); // Wait 3s then resolve. 
}); 

與您的代碼的問題是,你是返回一個承諾,那麼無極then內要創建一個又一個和期待當初的諾言等待它 - 恐怕這不是如何承諾工作。你將不得不做的承諾函數內所有的等待,然後調用決心:

編輯:這是不正確的,你可以延遲承諾鏈中任何then

function promise1() { 
 
    return new Promise((resolve) => { 
 
    setTimeout(() => { 
 
     console.log('promise1'); 
 
     resolve(); 
 
    }, 1000); 
 
    }) 
 
    .then(promise2); 
 
} 
 

 
function promise2() { 
 
    return new Promise((resolve) => { 
 
    setTimeout(() => { 
 
     console.log('promise2'); 
 
     resolve(); 
 
    }, 1000); 
 
    }); 
 
} 
 

 
function promise3() { 
 
    return new Promise((resolve) => { 
 
    setTimeout(() => { 
 
     console.log('promise3'); 
 
     resolve(); 
 
    }, 1000); 
 
    }); 
 
} 
 

 
promise1() 
 
    .then(promise3) 
 
    .then(() => { 
 
    console.log('...finished'); 
 
    })

但是,這不是一個等待css動畫的好方法。這是更好聽transitionend事件:

element.addEventListener('transitionend', onTransitionEnd); 
element.classList.add('transition-me'); 

請注意,如果您使用的是animation而不是transition的概念同樣適用,但使用animationend事件。

+0

在'$ timeout'上使用'setTimeout'不能解決_delay不是工作問題。但關於如何等待轉換的好建議。 :-) – sdgluck

+0

啊,我確實不喜歡動畫/轉換時間在css和js中重複。我絕對會考慮的! 關於你的建議:如果我在'添加數據到視圖'功能上添加一個超時,現在它在打開我的視圖之前等待,但它不會延遲第一個的調用('一切都準備就緒'),第二個然後('openAnotherView')。 我這樣調用解析:Promise.resolve(); – 11mb

+0

所以你想回報一個承諾,但你不想讓它等待? –